首页 新闻 会员 周边

关于 数据匹配的问题 真心求教

0
悬赏园豆:100 [已解决问题] 解决于 2012-03-16 17:14

问题如下:

  已经 result = 10, 数组 A = {1,2,3,4,5,6,7,8,9,10}, 希望能在数组中找出 SUM()= 10 的所有元素。

  比如: 1,9 ; 2,8; 10; 等等


  现在我想到的办法是 循环组合的方式: 从C(1,10) 到 C(2,10) 直到 C(10,10), 请问有没有更有效率的办法?


  在线等, 万分感谢!!!


补充: result 的值是不定的, 数组的长度是不定的, 内容也是不定的

rq1986的主页 rq1986 | 初学一级 | 园豆:110
提问于:2012-03-16 11:30
< >
分享
最佳答案
0

比较喜欢wanghui的算法,我格式化了一下,方便阅读,里面的新函数是限制乘数的比如求所有A+B=10的结果。

int NumberOfSetBits(int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
};
int main () {
int array [] = {1,2,3,4,5,6,7,8,9};
int length=9;
int requiredSum=10;
int multiplierCount=2;
int sum;
bool isLimiteMultiplierNum=false;
for (unsigned int i=1; i<(1<<length); ++i,sum=0) {//穷举乘数
if(isLimiteMultiplierNum&&NumberOfSetBits(i)!=multiplierCount)//限制乘数数量
continue;
for (unsigned int k=0; k<32; ++k)
{
if ((i&(1<<k)))//i的第k个bit位是否为1
{sum += array[k];}
}
if (sum==requiredSum){
for (unsigned int k=0; k<32; ++k )
{
if ((i&(1<<k)))
{
cout << array[k] << '';
}
}
cout << endl;
}
}
return 0;
}
收获园豆:5
today4king | 老鸟四级 |园豆:3499 | 2012-03-16 14:07
其他回答(3)
0

采用二分法来进行统计

az235 | 园豆:8483 (大侠五级) | 2012-03-16 11:56

二分法?  能具体点么?

支持(0) 反对(0) rq1986 | 园豆:110 (初学一级) | 2012-03-16 12:15

二分法首先要做一次排序,他这个不一定是有序的,排序的算法就不用说了,其实排序之后不需要使用二分法,借鉴归并排序的思想,前后两个指针,加和大了,后指针前移,小了,前指针后移,不过这样是和2个数加起来等于给定的数。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 12:23
3
int array [] = {1,2,3,4,5,6,7,8,9};
int length=9;

for (unsigned int i=1; i<(1<<length); ++i,sum=0) {
for (unsigned int k=0; k<32; ++k)
if ((i&(1<<k)))//i的第k个bit位是否为1
sum += array[k];
if (sum==requiredSum){
for (unsigned int k=0; k<32; ++k )
if ((i&(1<<k)))
cout << array[k] << '';
cout << endl;
}
}

输出:

1 2 3 4 
2 3 5
1 4 5
1 3 6
4 6
1 2 7
3 7
2 8
1 9

测试代码在这里

收获园豆:90
zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 12:05

哥  谢谢你的代码 ,  不过 你这个算是定制么?

支持(0) 反对(0) rq1986 | 园豆:110 (初学一级) | 2012-03-16 12:28

@rq1986: 

当然不是定制的了,array换成你的数组,length换成你的数组长度。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 12:58

@Wang Hui: 真心没看懂。。。能解释下算法么,我只看出 sum += array[k]; 会数组越界啊。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-03-16 13:25

@水牛刀刀: 

1:这里是采用一个整数才表示集合的子集。

任何一个集合含有N个元素,那么子集数目就是2的N次方。这里假定集合的数目小于32,那么就可以用一个整数来表示每一个集合。比如3,其二进制是:0000 0000 0000 0011,表示3代表的集合里含有位置为0和1的数组元素(位置0和1处的二进制数是1)。那么对于含有N个元素的集合,用[1-2的N次方]里的每一个数字就可以表示每一个集合,这里需要稍微理解一下。

2: 算法有一定的局限性,要求数组的长度小于32,如果数组长度太大,时间复杂度会非常高,而且没有办法降低。当然,如果一定要计算大于32个元素的,也可以。

3:外层for循环: for (unsigned int i=1; i<(1<<length); ++i,sum=0)

这里循环遍历[1-2的N次方]中的每一个数字,也就相当于遍历每一个集合。

4:内存循环:for (unsigned int k=0; k<32; ++k)

这里遍历集合内部的元素,:

if ((i&(1<<k)))//i的第k个bit位是否为1
sum += array[k];
i&(1<<k):是取得i的第k个bit位的数值,如果是1,说明集合中含有这个位置的元素,则sum += array[k];加和。
这里不会越界,因为如果你的数组程度是length,而length小于32,则2的length次方在第length+1位置上的二进制数字一定是0,不会进行array[k]操作。

下面就简单了,如果和等于要求的数值,则将当前的集合输出,当前的集合就是i表示的,而集合i中的元素就是i的二进制位为1的位置对应的的数组元素。

当你的数组长度不是很长的时候效率还是比较高的,如果数组长度大于32,比如说超过32而小于64,则将外层循环的i换位64位的,将内存循环的32换为64。

上面代码还有可以优化的地方,你可以想一下,不过如果数组的长度太大,时间复杂度会很高,而且我不认为有什么方法可以降低时间复杂度。
支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 13:39

@Wang Hui: 理解了,关键是第一步,用一个整数来表示一个集合的组合情况,确实是一种很好的算法。我的解法是用递归,感觉性能要比这种方法低,学习了。

支持(0) 反对(0) 水牛刀刀 | 园豆:6350 (大侠五级) | 2012-03-16 14:08

@Wang Hui: 写的真不错,从效率上来说确实非常快,我格式化了下贴在下面。

支持(0) 反对(0) today4king | 园豆:3499 (老鸟四级) | 2012-03-16 14:10

@今昭: 谢谢^^

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 14:24

@水牛刀刀: 我测试了一下,数组长度15时,效率还是可以接受的,再长点,就麻烦了。其实这个算法可以优化的,可以剔除一部分不符合要求的集合,效率应该可以提升一些。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 14:26

@Wang Hui: 

非常感谢你的解答, 我用的是C#,   for (unsigned int i=1; i<(1<<length); ++i,sum=0)  这句话应该怎么翻译呢?

支持(0) 反对(0) rq1986 | 园豆:110 (初学一级) | 2012-03-16 14:55

@Wang Hui: 

上面的问题我解决了   

另外一个问题  “这里假定集合的数目小于32”    这个32  是怎么来的?

按照我给的示例  所有的集合应该是  2的9次方 对么?

支持(0) 反对(0) rq1986 | 园豆:110 (初学一级) | 2012-03-16 15:07

@rq1986: 

集合的数目小于32,这句话我写的有问题,应该是集合里的元素数目小于32. 因为我们在表示集合的使用使用的是一个int类型,有32位,如果你使用64位的int,那么就可以表示64个元素的集合了。


按照你给的:A = {1,2,3,4,5,6,7,8,9,10},数组A有10个元素,子集数目是2的10次方。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 15:28

@rq1986: C#也是一样的,C#中对应的类型就是uint,至于移位操作都是一样的。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 15:29

@Wang Hui: 

谢谢你的回复 和 耐心。

我在尝试 做64个元素的时候,  i << length 这个位置  要溢出 高位舍弃后, 数据就不对了,  这里应该怎么做呢?

支持(0) 反对(0) rq1986 | 园豆:110 (初学一级) | 2012-03-16 16:04

@rq1986: 

我劝你不要尝试64个元素的计算,26个元素就要计算1年的时间,你还想尝试64个吗?

这个问题的资料可以看看这里:

Subset sum problem

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-03-16 16:33
0

完整代码:

        static HashSet<string> results = new HashSet<string>();
static Stack<int> currentResult = new Stack<int>();

static void TryResult(int[] numbers, int target)
{
if (target == 0)
{
results.Add(string.Join(",", currentResult.OrderBy(n => n)));
}
else
{
var candidates = numbers.Where(n => n <= target);
foreach (var candidate in candidates)
{
currentResult.Push(candidate);
TryResult(numbers.Where(n => n != candidate).ToArray(), target - candidate);
currentResult.Pop();
}
}
}

static void Main(string[] args)
{
var result = 10;
var numbers = Enumerable.Range(1, 10).ToArray();

TryResult(numbers, result);

foreach (var r in results)
{
Console.WriteLine(r);
}

Console.Read();
}

当数据量很大的时候可能会很耗时,最好做下尾递归优化。

收获园豆:5
水牛刀刀 | 园豆:6350 (大侠五级) | 2012-03-16 12:58
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册