找回密码
 注册
Simdroid-非首页
查看: 135|回复: 27

如何判断一个序列中有几个连续的数字?

[复制链接]
发表于 2014-10-29 20:47:29 | 显示全部楼层 |阅读模式 来自 山东威海
在matlab中文论坛发现一个问题,觉得挺有意思,大家可以探讨一下:
假如有一个数组[1,0,0,1,1,1,1,0,1,1,1,0,0,0,1]‘这样的
那怎样用matlab编程判断有连续3个以上的1所在的地方?


winner245给出了不错的solution,
  1. k = find(diff([0,m,0])).';
  2. g = find(k(2:2:end)- k(1:2:end)>=3);
  3. [k(2*g-1),k(2*g)-1]
复制代码





我也抛砖引玉一个:


  1. y=regexprep(num2str(m),' ','')
  2. [a,b] = regexp(y,'1{3,}','match')
  3. [b;b+cellfun(@numel,a)-1]
复制代码
那么问题就来了:
1:如果需要要求的不仅仅是1,是其他数字如:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,]
复制代码



需要判断连续的数字,该如何判断?
2:
如果再有所变化:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345]
复制代码
改如何找出 1,连续的数值;2、数据的位置;3、数据的个数

















发表于 2014-10-30 02:07:14 | 显示全部楼层 来自 英国
Simdroid开发平台
抛砖引玉:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
  2. cc = regexp(sprintf('%d ', m), '\<(\d+\s)\1*', 'match');

  3. RunLength = cellfun('length', regexp(cc, ' '));
  4. RunLengthCumSum = [0 cumsum(RunLength)];
  5. IfValid = cellfun('isempty', regexp(cc, '^0')) & RunLength>2;

  6. StartEnd = [RunLengthCumSum(IfValid)+1; RunLengthCumSum([ false IfValid])]
  7. ValueList  = m(StartEnd(1, :))

复制代码

  1. StartEnd =

  2.      4     9    15
  3.      7    11    19


  4. ValueList =

  5.            1           2        2345
复制代码

点评

那去掉IfValid前半部分就行啊,更简单  发表于 2014-10-31 00:38
如果连续的几个0也是想要的结果呢  发表于 2014-10-30 16:48
回复 1 不支持 0

使用道具 举报

发表于 2014-10-30 02:14:46 | 显示全部楼层 来自 英国
之前那个全是1或0的也可以不用cellfun:

  1. m = [1,0,0,1,1,1,1,0,1,1,1,0,0,0,1];
  2. [cc,dd] = regexp(sprintf('%d', m), '1{3,}', 'start', 'end');
  3. [cc;dd]
复制代码

点评

技术了得!  发表于 2014-10-30 07:56

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2014-10-30 14:39:11 | 显示全部楼层 来自 北京
m ~=0;
regionprops(m,'Area','PiexlIDlist');
简单直接,二维的一样适用。

点评

利用regionprops的PixelIdxList属性,吴兄处理非常巧妙!  发表于 2014-10-30 19:10

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2014-10-30 21:39:17 | 显示全部楼层 来自 加拿大
看了各位的解答确实大开眼界!我还是继续给出老土的 diff 解决方法:

reshape(find(diff([0 ~diff(m) 0])),2,[])

与诸位不同的是,此法可以把连续的0的位置也找出来(即不仅仅是找连续非0元素,这也是 ljelly 老兄提到的情形)
回复 不支持

使用道具 举报

发表于 2014-10-30 22:33:06 | 显示全部楼层 来自 加拿大
如果仅仅限制在找非0连续元素,那么 diff 方法至少有两种修改方法:

1. 预处理法
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
  2. id = m==0;
  3. m(id) = rand(1,sum(id));
  4. k = reshape(find(diff([0 ~diff(m) 0])),2,[])
  5. m(k(1,:))
复制代码


k =
     4     9    15
     7    11    19

ans =
           1           2        2345


2. 后修正法
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
  2. k = reshape(find(diff([0 ~diff(m) 0])),2,[])
  3. id = m(k(1,:))~=0;
  4. k(:,id)
  5. m(k(1,id))
复制代码

k =
     4     9    15
     7    11    19

ans =
           1           2        2345
回复 不支持

使用道具 举报

发表于 2014-10-31 00:21:11 | 显示全部楼层 来自 加拿大
本帖最后由 winner245 于 2014-10-31 04:05 编辑

刚才我在 5L、6L 考虑的是任意连续数字情形,并没有规定连续数字的个数 >= 3,只是找出连续的数值(这是MATLAB中文论坛最近另一个帖子的问题:http://ilovematlab.cn/thread-308583-1-1.html )。

如果加上连续元素个数 >= 3 的限制,我还是先一如既往 diff 到底(对5L、6L代码修改如下):

1. 连续0元素计入在内:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345]
  2. k = reshape(find(diff([0 ~diff(m) 0])),2,[]);
  3. k = k(:,diff(k)>=2)
  4. m(k(1,:))
复制代码

k =
     4     9    12    15
     7    11    14    19

ans =
           1           2           0        2345

2. 仅考虑非0连续元素:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
  2. k = reshape(find(diff([0 ~diff(m) 0])),2,[]);
  3. k = k(:,diff(k)>=2 & m(k(1,:))~=0)
  4. m(k(1,:))
复制代码

k =
     4     9    15
     7    11    19

ans =
           1           2        2345


受两位版主启发,可以将 diff 和 regexp 结合使用
1. 连续0元素计入在内:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
  2. [c,d] = regexp(sprintf('%d', ~diff(m)), '1{2,}');
  3. [c;d+1]
  4. m(c)
复制代码

ans =
     4     9    12    15
     7    11    14    19

ans =
           1           2           0        2345

2. 仅考虑非0连续元素:
  1. m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
  2. [c,d] = regexp(sprintf('%d', ~diff(m)), '1{2,}');
  3. id = m(c)~=0;
  4. [c(id);d(id)+1]
  5. m(c(id))
复制代码

ans =
     4     9    15
     7    11    19

ans =
           1           2        2345

点评

独到深刻!  发表于 2014-10-31 11:27
独到深刻!  发表于 2014-10-31 11:27
独到深刻!  发表于 2014-10-31 11:27
刘兄总结的好,网友们受益了,鼓掌  发表于 2014-10-31 09:01

评分

2

查看全部评分

回复 不支持

使用道具 举报

发表于 2014-10-31 04:06:46 | 显示全部楼层 来自 加拿大
等效的 diff + regexp 法:

>> m = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
>> [c,d] = regexp(sprintf('%d', abs(diff(m))), '0{2,}');  
>> [c;d+1]   % 连续0元素计入在内
>> m(c)         
>> id = m(c)~=0;  % 仅考虑连续非0元素
>> [c(id);d(id)+1]
>> m(c(id))

ans =
     4     9    12    18
     7    11    14    22

ans =
           1           2           0        2345

ans =
     4     9    18
     7    11    22

ans =
           1           2        2345


点评

有道理,字符串的处理速度要比矩阵或者向量慢一些,此题目贴在cody上了已经  发表于 2014-11-1 22:41
cody 上检验代码好坏似乎仅仅根据代码的 size,而 size 小却未必效率高,regexp 功能强大,使用方便,可能会在 size 上有优势,但效率上不会有什么优势  发表于 2014-11-1 05:37
如果单就代码效率的话,个人认为纯粹基于 diff 的方法优于涉及 regexp 的方法,后者 regexp 函数功能强大,而 diff 功能简单,二者的效率肯定是 diff 更高  发表于 2014-11-1 05:34
大家感兴趣的话,可以把此问题出在mathworks的cody上,看这些种方法的size如何,速度如何,再看看其他人有没有别的解法  发表于 2014-10-31 09:02
回复 不支持

使用道具 举报

 楼主| 发表于 2014-11-1 22:45:18 | 显示全部楼层 来自 山东威海
题目已经贴在了matlab cody上面了
大家可以贴一下,也可以看看别人的解法题目:2651
回复 不支持

使用道具 举报

发表于 2014-11-2 08:25:12 | 显示全部楼层 来自 加拿大
liuyalong008 发表于 2014-11-1 22:45
题目已经贴在了matlab cody上面了
大家可以贴一下,也可以看看别人的解法题目:2651
...

不知道那个 size = 27 的代码怎么写出来的,我 beat 不了他所以看不见他的代码,我只能写到 29

x = [1,0,0,1,1,1,1,0,2,2,2,0,0,0,2345,2345,2345,2345,2345];
[a, b] = regexp(char(x), '(.)\1+');
y = [a; b; x(a)]
回复 不支持

使用道具 举报

 楼主| 发表于 2014-11-2 08:46:56 | 显示全部楼层 来自 山东威海
winner245 发表于 2014-11-2 08:25
不知道那个 size = 27 的代码怎么写出来的,我 beat 不了他所以看不见他的代码,我只能写到 29

x = [1,0 ...
  1.   [s, e] = regexp(char(x), '(.)\1+');
  2.   [s; e; x(s)];
复制代码

输出时用ans即可
回复 不支持

使用道具 举报

发表于 2014-11-2 09:42:02 | 显示全部楼层 来自 加拿大

你的意思是这个子函数写成:

function ans = successive_value(x)
    [s, e] = regexp(char(x),  '(.)\1+');
    [s;e;x(s)];
end

日常生活中我倒是从来没想过这样写代码,不过,cody只以size大小衡量代码的优劣,这确实是一个减小size的技巧。

但就代码效率而言,这个size=27的代码,反倒不如我最初在5L给出的 size=37 的代码:
  1. function main1
  2. clear all
  3. close all
  4. clc   
  5. x = randi([0,100],1,1e5);
  6. [timeit(@()fun1(x)); timeit(@()fun2(x))]
  7. isequal(fun1(x),fun2(x))
  8. end
  9. function y = fun1(x)
  10.     a = reshape(find(diff([0 ~diff(x) 0])),2,[]);
  11.     y = [a; x(a(1,:))];
  12. end
  13. function ans = fun2(x)
  14.     [s, e] = regexp(char(x),  '(.)\1+');
  15.     [s;e;x(s)];
  16. end
复制代码

ans =
    0.0008
    0.0098

ans =
     1
似乎,cody这种只注重代码size的方法不太科学。纯属个人愚见,感谢指点

点评

嗯,赞同,祁版主不愧是玩cody的高手!  发表于 2014-11-4 06:31
官方给出一种科学的评判标准很难,命题者可以自己根据自己的意愿来设定size的评判标准,只需要修改score的计算方式即可  发表于 2014-11-3 13:19
回复 不支持

使用道具 举报

 楼主| 发表于 2014-11-2 10:58:44 | 显示全部楼层 来自 山东威海
winner245 发表于 2014-11-2 09:42
你的意思是这个子函数写成:

function ans = successive_value(x)

诚如君言,size评判有失偏颇,并且很多时候效率其实是优先考虑的,但是有时候在兼顾size的情况下也有很多巧妙和精彩的解法。也算是欣赏一下不同的技巧。
就此问题而言,老兄5L给出的solution应该差不多是最优的,估计没有太多其他算法出其右

点评

嗯,版主所言极是,在兼顾 size 的情况下确实会出现一些巧妙、令人意想不到解法,作为发散思维还是很有用的!  发表于 2014-11-2 20:28
回复 不支持

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|小黑屋|联系我们|仿真互动网 ( 京ICP备15048925号-7 )

GMT+8, 2024-4-19 12:22 , Processed in 0.070101 second(s), 22 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表