Версия PHP, вдохновленная версией C # Кейта Беллера.
PHP-версия bala не работала для меня, потому что мне не нужно было группировать числа. Мне нужна более простая реализация с одним целевым значением и пулом чисел. Эта функция также сократит любые повторяющиеся записи.
/**
* Calculates a subset sum: finds out which combinations of numbers
* from the numbers array can be added together to come to the target
* number.
*
* Returns an indexed array with arrays of number combinations.
*
* Example:
*
* <pre>
* $matches = subset_sum(array(5,10,7,3,20), 25);
* </pre>
*
* Returns:
*
* <pre>
* Array
* (
* [0] => Array
* (
* [0] => 3
* [1] => 5
* [2] => 7
* [3] => 10
* )
* [1] => Array
* (
* [0] => 5
* [1] => 20
* )
* )
* </pre>
*
* @param number[] $numbers
* @param number $target
* @param array $part
* @return array[number[]]
*/
function subset_sum($numbers, $target, $part=null)
{
// we assume that an empty $part variable means this
// is the top level call.
$toplevel = false;
if($part === null) {
$toplevel = true;
$part = array();
}
$s = 0;
foreach($part as $x)
{
$s = $s + $x;
}
// we have found a match!
if($s == $target)
{
sort($part); // ensure the numbers are always sorted
return array(implode('|', $part));
}
// gone too far, break off
if($s >= $target)
{
return null;
}
$matches = array();
$totalNumbers = count($numbers);
for($i=0; $i < $totalNumbers; $i++)
{
$remaining = array();
$n = $numbers[$i];
for($j = $i+1; $j < $totalNumbers; $j++)
{
$remaining[] = $numbers[$j];
}
$part_rec = $part;
$part_rec[] = $n;
$result = subset_sum($remaining, $target, $part_rec);
if($result)
{
$matches = array_merge($matches, $result);
}
}
if(!$toplevel)
{
return $matches;
}
// this is the top level function call: we have to
// prepare the final result value by stripping any
// duplicate results.
$matches = array_unique($matches);
$result = array();
foreach($matches as $entry)
{
$result[] = explode('|', $entry);
}
return $result;
}
В вашем последнем блоке else
вам нужно очистить «w» или другой недопустимый ввод от сканера. Вы можете сделать это, вызвав next()
на сканере и проигнорировав его возвращаемое значение, чтобы выбросить этот недопустимый вход, следующим образом:
else
{
System.out.println("You have entered an invalid input. Try again.");
in.next();
}
Переменные флагов слишком подвержены ошибкам для использования. Вместо этого используйте явный контроль цикла с комментариями. Кроме того, hasNextInt()
не блокируется. Это неблокирующая проверка, чтобы увидеть, может ли будущий вызов next
получить вход без блокировки. Если вы хотите заблокировать, используйте метод nextInt()
.
// Scanner that will read the integer
final Scanner in = new Scanner(System.in);
int inputInt;
do { // Loop until we have correct input
System.out.print("Specify an integer between 0 and 5: ");
try {
inputInt = in.nextInt(); // Blocks for user input
if (inputInt >= 0 && inputInt <= 5) {
break; // Got valid input, stop looping
} else {
System.out.println("You have not entered a number between 0 and 5. Try again.");
continue; // restart loop, wrong number
}
} catch (final InputMismatchException e) {
System.out.println("You have entered an invalid input. Try again.");
in.next(); // discard non-int input
continue; // restart loop, didn't get an integer input
}
} while (true);
Проблема заключалась в том, что вы не продвинули Scanner
мимо проблематичного ввода. Из документа hasNextInt()
:
Возвращает
true
, если следующий токен в этом входе сканера может быть интерпретирован как значениеint
в базовом значении по умолчанию, используяnextInt()
.Это относится ко всем методам
hasNextXXX()
: они возвращаютtrue
илиfalse
, не продвигаяScanner
.Вот фрагмент, иллюстрирующий проблему:
String input = "1 2 3 oops 4 5 6"; Scanner sc = new Scanner(input); while (sc.hasNext()) { if (sc.hasNextInt()) { int num = sc.nextInt(); System.out.println("Got " + num); } else { System.out.println("int, please!"); //sc.next(); // uncomment to fix! } }
Вы обнаружите, что эта программа перейдет в бесконечный цикл, повторно задав
int, please!
.Если вы раскомментируйте оператор
sc.next()
, затем онScanner
пройдет мимо маркера, который не работаетhasNextInt()
. Затем программа будет печатать:Got 1 Got 2 Got 3 int, please! Got 4 Got 5 Got 6
Тот факт, что сбой
hasNextXXX()
не пропускает вход, преднамерен: он позволяет выполнять дополнительные проверки этого токена, если это необходимо. Вот пример, иллюстрирующий:String input = " 1 true foo 2 false bar 3 "; Scanner sc = new Scanner(input); while (sc.hasNext()) { if (sc.hasNextInt()) { System.out.println("(int) " + sc.nextInt()); } else if (sc.hasNextBoolean()) { System.out.println("(boolean) " + sc.nextBoolean()); } else { System.out.println(sc.next()); } }
Если вы запустите эту программу, вы получите следующее:
(int) 1 (boolean) true foo (int) 2 (boolean) false bar (int) 3
Это утверждение Бен С. о неблокирующем вызове ложно:
Также hasNextInt () не блокирует. Это неблокирующая проверка, чтобы увидеть, может ли следующий следующий вызов получить вход без блокировки.
... хотя я знаю, что документация может быть легко ошибочно понята, чтобы дать это мнение, и само название подразумевает, что оно должно использоваться для этой цели , Соответствующая цитата с добавлением курсора:
Методы next () и hasNext () и их методы примитивного типа (такие как nextInt () и hasNextInt ()) сначала пропускают любой вход, который соответствует шаблону разделителя, а затем пытается вернуть следующий токен. Оба hasNext и следующие методы могут блокировать ожидание дальнейшего ввода. Независимо от того, блокирует ли блок hasNext метод, будет ли заблокирован его связанный следующий метод.
Это, конечно, тонкая точка. Либо говоря: «И hasNext, и следующие методы», либо «Как hasnext (), так и next ()» подразумевали бы, что методы компаньона будут действовать по-разному. Но, видя, что они соответствуют одному и тому же соглашению об именах (и документации, конечно), разумно ожидать, что они будут действовать одинаково, а hasNext () ясно говорит, что он может блокироваться.
Мета-заметка: вероятно, это комментарий к некорректной записи, но кажется, что в качестве нового пользователя я могу только опубликовать этот ответ (или отредактировать вики, которые, по-видимому, предпочтительны для ситлистических изменений, а не для веществ) .