Как мне найти все наборы из N однозначных неповторяющихся чисел, которые в сумме дают сумму в PHP? - PullRequest
7 голосов
/ 04 мая 2010

Допустим, я хочу найти все наборы из 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). Если это так, добавьте его в список возможных подходящих наборов.

Я делаю это для личного проекта, который поможет мне в решении головоломок Какуро, фактически не решая все сразу. Да, это может быть обманом, но это ... это не так уж плохо ...: P

Ответы [ 7 ]

2 голосов
/ 04 мая 2010

Наивным подходом было бы увеличение переменной с 12345 до 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";
}

Рабочий пример

1 голос
/ 04 мая 2010

Используя код комбинации из здесь

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 

Разве это не мило?

1 голос
/ 04 мая 2010

Это, вероятно, достаточно быстро:

<?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";
        }
}

?>
1 голос
/ 04 мая 2010
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";
    }
}
0 голосов
/ 29 октября 2010

Давайте напишем f (30,5,1) для ответа на вашу проблему. 30 обозначает желаемую сумму, 5 обозначает количество цифр, которое следует добавить к желаемой сумме, а 1 обозначает минимально допустимую цифру. В этой форме вы можете решить проблему рекурсивно. Например,

f (30,5, b) = сумма (i = 1,9) f (30-i, 4, i + 1)

Мы фактически исчерпали самое низкое значение, которое я нашел в комбинации, которую вы ищете. Если вы подумаете о максимально возможном значении i (оно не может быть слишком большим, поскольку это минимум цифр) и добавите некоторые подходящие условия спасения, тогда у вас будет очень быстрое решение. *

0 голосов
/ 04 мая 2010

Я считаю, что это известно как проблема суммы подмножеств:

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

0 голосов
/ 04 мая 2010

Я знаю, что есть алгоритмы для этого, и они, вероятно, будут предоставлены другими людьми, но вот одно простое упрощение, которое вы можете сделать: искать все наборы из 4 однозначных цифр, которые в сумме составляют 21-29 (я предполагаю вы не считаете 0 цифрой), а просто исключаете те, для которых 30- (сумма) является одной из цифр.

Если бы я хотел попробовать что-то еще быстрее, я бы подумал начать с 45678 и постепенно изменять его, добавляя 1 к цифре и вычитая 1 из другой цифры. Не уверен, насколько хорошо это будет работать в конце концов.

...