7

假设我想找到所有 5 个单数、非重复数字,加起来为 30...我最终会得到 [9,8,7,5,1], [9,8,7 ,4,2], [9,8,6,4,3], [9,8,6,5,2], [9,7,6,5,3] 和 [8,7,6, 5,4]。这些集合中的每一个都包含 5 个不重复的数字,它们加起来为 30,即给定的总和。

任何帮助将不胜感激。即使只是我使用的一个起点也很棒。

我想出了一种方法,这似乎还有很长的路要走:获取所有唯一的 5 位数字(12345、12346、12347 等),将数字相加,看看它是否等于给定的总和(例如 30)。如果是,请将其添加到可能的匹配集列表中。

我这样做是为了一个个人项目,这将帮助我解决 Kakuro 难题,而无需一次真正解决整个问题。是的,它可能是作弊,但它......它不是那么糟糕......:P

4

8 回答 8

2

一种天真的方法是从12345until增加一个变量,98765并仅在它具有唯一数字且数字总和为 时才选择它30

for($i=12345;$i<98765;$i++) {
    $arr = preg_split('//',strval($i));
    if(count(array_unique($arr)) == count($arr) && array_sum($arr) == 30)
        echo $i."\n";
}

工作示例

于 2010-05-04T02:07:05.420 回答
1
function sumOfDigits($num) {
    $str = "{$num}";
    $sum = 0;
    for ($i=0;$i<strlen($str);$i++) {
        $sum += (int)substr($str, $i, 1);
    }
    return $sum;
}

function hasDuplicateDigits($num) {
    $str = "{$num}";
    $pieces = array();
    for ($i=0;$i<strlen($str);$i++) {
        $pieces[] = substr($str, $i, 1);
    }
    return (count(array_unique($pieces)) != strlen($str));
}

// if you prefer the opposite function
function hasAllUniqueDigits($num) {
    return (!hasDuplicateDigits($num));
}

$numbers = range(10000, 99999);

foreach ($numbers as $num) {
    if ( !hasDuplicateDigits($num) && (sumOfDigits($num) == 30)) {
        print $num . "\n";
    }
}
于 2010-05-04T02:16:34.977 回答
1

这可能足够快:

<?php

$digitCount = 5;
$sum = 30;

function getAnswer($b)
{
        $a = "";
        $i = 1;
        while ($b)
        {
                if ($b & 1) $a .= "$i ";

                $b >>= 1;
                ++$i;
        }
        return $a;
}

for ($b = 0; $b < 512; ++$b)
{
        $v = 0;
        $c = 0;
        $i = 1;

        $s = $b;
        while ($s)
        {
                if ($s & 1)
                {
                        if (++$c > $digitCount) continue 2;
                        $v += $i;
                }
                $s >>= 1;
                ++$i;
        }

        if ($c == $digitCount && $v == $sum)
        {
                echo getAnswer($b)."\n";
        }
}

?>
于 2010-05-04T02:55:06.733 回答
1

从这里使用组合代码

foreach(new Combinations("123456789", 5) as $p)
    $r[array_sum(str_split($p))] .= "$p ";
print_r($r); 

结果

[15] => 12345 
[16] => 12346 
[17] => 12347 12356 
[18] => 12348 12357 12456 
[19] => 12349 12358 12367 12457 13456 
[20] => 12359 12368 12458 12467 13457 23456 
[21] => 12369 12378 12459 12468 12567 13458 13467 23457 
[22] => 12379 12469 12478 12568 13459 13468 13567 23458 23467 
[23] => 12389 12479 12569 12578 13469 13478 13568 14567 23459 23468 23567 
[24] => 12489 12579 12678 13479 13569 13578 14568 23469 23478 23568 24567 
[25] => 12589 12679 13489 13579 13678 14569 14578 23479 23569 23578 24568 34567 
[26] => 12689 13589 13679 14579 14678 23489 23579 23678 24569 24578 34568 
[27] => 12789 13689 14589 14679 15678 23589 23679 24579 24678 34569 34578 
[28] => 13789 14689 15679 23689 24589 24679 25678 34579 34678 
[29] => 14789 15689 23789 24689 25679 34589 34679 35678 
[30] => 15789 24789 25689 34689 35679 45678 
[31] => 16789 25789 34789 35689 45679 
[32] => 26789 35789 45689 
[33] => 36789 45789 
[34] => 46789 
[35] => 56789 

是不是很可爱?

于 2010-05-04T09:35:29.793 回答
0

我知道有这方面的算法,它们可能会由其他人提供,但这里有一个你可以做的快速简化:查找所有 4 个个位数加起来为 21-29 的集合(我假设你是不把 0 算作一个数字),只消除那些 30-(总和)是其中一个数字的数字。

如果我想更快地尝试一些东西,我会考虑从 45678 开始,并通过将一个数字加 1 并从另一个数字减 1 来逐步改变它。不过,不确定最终效果如何。

于 2010-05-04T01:51:56.990 回答
0

我相信这被称为子集和问题:

http://mathworld.wolfram.com/SubsetSumProblem.html

于 2010-05-04T13:44:47.530 回答
0

让我们写 f(30,5,1) 来回答您的问题。30 表示所需的总和,5 表示应添加到所需总和的位数,1 表示可接受的最小位数。在这种形式下,您可以递归地解决问题。例如,

f(30,5,b) = 总和(i = 1..9) f(30-i,4,i+1)

我们实际上已经耗尽了您所寻求的组合中出现的最低值。如果您更仔细地考虑 i 的最大可能值(它不能太大,因为它是数字中的最小值),并添加一些适当的救助条件,那么您将获得一个非常快速的解决方案。

于 2010-10-29T12:10:01.173 回答
0

打印数字总和形成一个由 n 个数字组成的数组,其中没有重复的数字。例如输入:[100, 213, 414, 555, 62, 321]

输出:596(即 213+62+321)

于 2021-10-19T10:50:44.613 回答