实际上,这个问题可以概括如下:
从满足的给定元素集中查找所有可能的组合 一定的标准。
那么,有什么好的算法吗?
答案 0 :(得分:9)
只有16种可能性(其中一种是将“它们都没有”加在一起,这不会给你24),所以老式的“蛮力”算法看起来对我很好:< / p>
for (unsigned int choice = 1; choice < 16; ++choice) {
int sum = 0;
if (choice & 1) sum += elements[0];
if (choice & 2) sum += elements[1];
if (choice & 4) sum += elements[2];
if (choice & 8) sum += elements[3];
if (sum == 24) {
// we have a winner
}
}
在问题的完全一般形式中,判断组合是否符合“特定标准”的唯一方法是评估每个组合的标准。如果有关标准的更多信息,也许您可以通过一些方法来避免测试每个组合并相应地构建算法,但不能没有这些细节。再一次,蛮力为王。
答案 1 :(得分:2)
Wikipedia和MathWorld中有关于总和问题的两个有趣解释。
在您提出的第一个问题的情况下,第一个答案对于有限数量的元素是有益的。你应该意识到Jessop先生使用16作为他的循环边界的原因是因为这是2 ^ 4,其中4是你集合中元素的数量。如果你有100个元素,那么循环限制将变为2 ^ 100,你的算法将永远完成。
如果是有界总和,则应考虑深度优先搜索,因为当元素总和超过您要查找的总和时,您可以修剪分支并回溯。
在通用问题的情况下,找到满足特定标准的元素子集,这称为背包问题,已知为NP-Complete。鉴于此,没有算法可以在不到指数的时间内解决它。
然而,有几种启发式方法可以带来很好的结果,包括(但不限于)遗传算法(我个人喜欢,因为我写了一本关于它们的书)和动态编程。在Google中进行的简单搜索将显示许多科学论文,这些论文描述了针对此问题的不同解决方案。
答案 2 :(得分:1)
从给定的元素集中查找所有可能的组合 符合某个标准
如果我理解你,这段代码对你有帮助:
>>> from itertools import combinations as combi
>>> combi.__doc__
'combinations(iterable, r) --> combinations object\n\nReturn successive r-length
combinations of elements in the iterable.\n\ncombinations(range(4), 3) --> (0,1
,2), (0,1,3), (0,2,3), (1,2,3)'
>>> set = range(4)
>>> set
[0, 1, 2, 3]
>>> criteria = range(3)
>>> criteria
[0, 1, 2]
>>> for tuple in list(combi(set, len(criteria))):
... if cmp(list(tuple), criteria) == 0:
... print 'criteria exists in tuple: ', tuple
...
criteria exists in tuple: (0, 1, 2)
>>> list(combi(set, len(criteria)))
[(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]
答案 3 :(得分:0)
一般来说,对于一个问题,你必须尝试所有的姿势,如果你知道它不符合标准,你应该做的事情让代码中止组合的建立(如果你的标准是你没有更多那么两个蓝色的球,然后你必须中止有两个以上的计算)。 Backtracing
def perm(set,permutation):
if lenght(set) == lenght(permutation):
print permutation
else:
for element in set:
if permutation.add(element) == criteria:
perm(sett,permutation)
else:
permutation.pop() //remove the element added in the if
答案 4 :(得分:0)
输入数字的集合很重要,正如您可以在你的开始集中的负数,虚数,有理数等。你也可以限制为例如所有偶数,所有奇数输入等。
这意味着很难构建一些演绎效果。你需要蛮力,a.k.a.尝试每一个组合等。
在这个特殊的问题中你可以建立一个递归的算法 - 例如找到3个Int(1,22)的每个组合,最多可加23个,然后加1个,每个组合加22个并加2个等等。这可以再次分成2个加起来21个等的每个组合。需要决定你是否可以计算两次相同的数字。
一旦你有了一个递归函数来调用 -
combinations( 24 , 4 ) = combinations( 23, 3 ) + combinations( 22, 3 ) + ... combinations( 4, 3 );
combinations( 23 , 3 ) = combinations( 22, 2 ) + ... combinations( 3, 2 );
等
这很有效,除了你必须小心在递归中重复数字。
答案 5 :(得分:0)
private int[][] work()
{
const int target = 24;
List<int[]> combos = new List<int[]>();
for(int i = 0; i < 9; i++)
for(int x = 0; x < 9; x++)
for(int y = 0; y < 9; y++)
for (int z = 0; z < 9; z++)
{
int res = x + y + z + i;
if (res == target)
{
combos.Add(new int[] { x, y, z, i });
}
}
return combos.ToArray();
}
它立即起作用,但可能有更好的方法,而不是'猜测和检查'。我正在做的就是遍历各种可能性,将它们全部加在一起,看看它是否达到了目标值。
答案 6 :(得分:0)
如果我正确理解你的问题,你所要求的是“排列”或可能的排列(X)数字的数字(N)从一组(Y)数字中取出。
N = Y! / (Y - X)!
我不知道这是否会有所帮助,但这是我想出的关于排列作业的解决方案。
您使用substr函数
输入:123(字符串)1)将每个输入数字放入一个数组
array[N1,N2,N3,...]
2)创建交换功能
function swap(Number A, Number B)
{
temp = Number B
Number B = Number A
Number A = temp
}
3)此算法使用交换函数移动数字,直到完成所有排列。
original_string= '123'
temp_string=''
While( temp_string != original_string)
{
swap(array element[i], array element[i+1])
if (i == 1)
i == 0
temp_string = array.toString
i++
}
希望您可以遵循我的伪代码,但这至少适用于3位数的排列
答案 7 :(得分:0)
(n X n) 建立了一个nxn
的方阵并将所有相应的交叉值打印在一起
e.g。
1 2 3 4
1 11 12 13 14
2 .. .. .. ..
3 ..
4 .. ..