0

Предположим, я хочу бесконечно перебирать несколько целых значений, пока не найдется их подходящая комбинация. Если значение одно, то это делается легко: мы просто перебираем циклом while это значение. Что делать, если этих значений несколько? P.S. думаю, это можно сделать с помощью BFS, но нет ли более простого способа?

UPD: Например, у меня есть целочисленная переменная a и функция check(int a), возвращающая true если это значение подходит. Перибирать все значения в данном случае можно так:

int a = 0;
while(!check(a) && !check(-a)) {
    ++a;
}

Что делать, если у меня есть целочисленные переременные a, b и функция check(int a, int b)? Как перебирать?

KotFind
  • 91
  • 1
    я один ничего не понял ? – Stranger in the Q Jan 20 '20 at 14:42
  • @StrangerintheQ Нет, вы не одиноки :) Я тоже в недоумении... – Harry Jan 20 '20 at 14:43
  • Это что-то типа факторизации полным перебором что ли?) – iksuy Jan 20 '20 at 14:45
  • Например, у меня есть целочисленная переменная a и функция check(int a), возвращающая true если это значение подходит. Перибирать все значения в данном случае можно так: int a = 0; while(!check(a) && !check(-a)) { ++a; } (код на яыке c++). Что делать, если у меня есть целочисленные переременные a, b и функция check(int a, int b)? Как перебирать? – KotFind Jan 20 '20 at 14:49
  • 1
    Может два вложенных цикла, отдельно по "a" и "b"? И это, внесите код в вопрос, невозможно же читать. :-( – pepsicoca1 Jan 20 '20 at 14:53
  • Два вложеных цикла будут работать, если мы знаем границы для a или b. Иначе мы будем бесконечно перебирать b, а a так и будет оставаться нулем. – KotFind Jan 20 '20 at 14:56
  • Если у Вас бесконечное количество значений "a", то Вам все равно, какое "b". Пусть даже "b" у Вас всегда будет нулем, переберите-ка сначала БЕСКОНЕЧНОЕ количество значений "a" с нулевым "b", а потом будете думать, как "b" сменить. :-) И да, для целых "a" бесконечности не будет, переполнится разрядная сетка когда-нибудь и "a" перекинется в ноль опять. :-) – pepsicoca1 Jan 20 '20 at 14:59
  • Надо какое-то ещё условие наложить по-хорошему на задачу. Например, b<=a и тогда перебирать во внутреннем цикле b только от 1 до a, например. – CrazyElf Jan 20 '20 at 15:05
  • @KotFind Вы хотите что то подобное: https://ru.stackoverflow.com/q/581668 ? – Mike Jan 20 '20 at 15:12
  • Я хочу переберать так, чтобы оба значения изменялись с примерно одинаковой скоростью. Как я уже писал, можно использовать BFS. Т.е. (для двух значений) мы предполагаем, что находимся на клетчатом поле в клетке (0, 0) потом рассматриваем соседние для этой клетки клетки, потом соседние для соседей и т.д. Но этот способ мне не очень нравится. Ему нужно достаточно много дополнительной памяти, да и код некрасивым будет. – KotFind Jan 20 '20 at 15:15
  • @Mike нет. В вашем примере. Есть заданный массив, у меня же бесконечная последовательность чисел. – KotFind Jan 20 '20 at 15:30
  • 1
    Господи, ну напишите так, чтоб оно по кругу ходило! "змейкой", так сказать - что, так сложно?... Вон, на каких угодно языках :) - вам остается только направление поменять. – Harry Jan 20 '20 at 15:36
  • @KotFind Для бесконечной последовательности чисел и выполнение алгоритма будет бесконечным. Есть два способа останова: 1. решаете остановиться после определенного количества найденных чисел. 2. заранее, математически вычисляете возможные границы (и возможно конкретные числа, которые вообще стоит проверять) перебора. И второй способ всецело зависит от того, что именно делает check. – Mike Jan 20 '20 at 16:11
  • @Harry спасибо. То, что нужно. – KotFind Jan 20 '20 at 16:18
  • Если значение одно, то это делается легко: мы просто перебираем циклом while это значение. Что делать, если этих значений несколько? WHILE позволяет перебирать любое количество любых параметров с любыми зависимостями. Просто в завершении будет выражение немножко сложнее, чем ++a... – Akina Jan 20 '20 at 16:37

3 Answers3

3

Псевдокод:

M = 0

Повторять:
    для a от 0 до M:
        b = M
        Проверить (a, b)
    для b от 0 до (M-1)
        a = M
        Проверить (a, b)
    M = M + 1

То есть это похоже на то, что вы пишете:

Т.е. (для двух значений) мы предполагаем, что находимся на клетчатом поле в клетке (0, 0) потом рассматриваем соседние для этой клетки клетки, потом соседние для соседей и т.д. Но этот способ мне не очень нравится. Ему нужно достаточно много дополнительной памяти, да и код некрасивым будет.

Только в моём варианте не надо хранить список клеток и искать их соседей, потому что каждая итерация соседей будет представлять из себя полоски клеток сверху и справа от квадрата, который на каждой итерации увеличивается на 1.

Для более двух переменных тот же принцип, но код немного посложнее.

UPD: Понял, что можно сделать ещё проще:

S = 0
Повторять:
    для a от 0 до S:
        b = S - a
        Проверить (a, b)
    S = S + 1
Xander
  • 20,499
2

Когда вы перебирали значения одной переменной, то вы просто двигались по оси OX от нуля в сторону положительных значений до бесконечности. И с этим проблем нет, бери да перебирай.

Теперь вам надо перебирать два числа. Т.е. как-то одновременно двигаться по двумя осям координат "закрашивая" всю проверенную область. Как вы будете её закрашивать - ваше дело, но если нужен простой перебор всех вариантов в "лоб", то двигаться только вдоль одной оси - не выйдет: она же бесконечная. Поэтому можно попробовать двигаться по диагонали заливая всё от угла 0,0 в сторону угла +бесконечность_по_X,+бесконечность_по_Y.

Путь, который пройдёт ваш алгоритм можно представить как на картинке ниже:

графическое представление решения

А сам код (мой пример на php) может быть, например, таким (в переменной dir - направление движения - или в сторону оси a или в сторону оси b, а остальное понятно и так):

const DIR_A = 0;
const DIR_B = 1;

$a = 0;
$b = 0;
$dir = DIR_A;

$dots = [];
while (count($dots) < 16) {
    $dots[] = [$a, $b];
    if ($dir === DIR_A) {
        if ($b > 0) {
            $b--;
            $a++;
        } else {
            $a++;
            $dir = DIR_B;
        }
    } else {
        if ($a > 0) {
            $a--;
            $b++;
        } else {
            $b++;
            $dir = DIR_A;
        }
    }
}

foreach ($dots as $dot) {
    echo "[{$dot[0]}, {$dot[1]}], ";
}
echo "...\n";

В итоге получается вот такой вывод:

[0, 0], [1, 0], [0, 1], [0, 2], [1, 1], [2, 0], [3, 0], [2, 1], [1, 2], [0, 3], [0, 4], [1, 3], [2, 2], [3, 1], [4, 0], [5, 0], ...
Lexx918
  • 2,779
0

Ну, если ты хочешь перебирать несколько значений в поисках подходящей их комбинаций, то ... эээ ... может цикл в цикле сделать ?!

bool isFound = false;

for (int a = start; a < end; a++) {
  for (int b = start; b < end; b++) {
    ...
      if (check(a, b, ..., n)) {
        // Сохрани найденные значения
        isFound = true;
        break;
      }
    ...
    if (isFound) break;
  }
  if (isFound) break;
}

Только не перебирай до бесконечности! Это как-то долго что ли...

  • В том то и проблемма, что я не знаю, как далеко числа a и b находятся от значения start (т.е. я не знаю end). В вашей же релизации, если не зать end, то a будет перебираться до бесконечности, а b так и останется со значением start. – KotFind Jan 20 '20 at 15:27
  • Парень, в компьютере всё конечное. Для int максимальное значение - чуть больше двух миллиардов. Посмотри в справочнике. Для uint - чуть больше четырёх миллиардов. Думаю, тебе же не нужно, чтобы значения пошли перебираться по второму, третьему кругу? – Євген Діулін Jan 20 '20 at 15:34
  • Значения start и end задаются тобой самостоятельно. Чего ты там не знаешь? Какое число ты туда напишешь? Пиши любое в пределах допустимого – Євген Діулін Jan 20 '20 at 15:35
  • Ок. Функция check(...) работает очень долго, а мне нужно проверить как можно больше различных значений за конечное время, так чтобы все аргументы менялись с примерно одинаковой скоростью. – KotFind Jan 20 '20 at 16:17
  • За конечное время - конечное число значений. Смирись. Если она работает долго - оптимизируй. Если возможно, то не делай итерацию по всем значениям: если первый аргумент заведомо неподходящ - не начинай итерировать второй. – Євген Діулін Jan 20 '20 at 16:42
  • А вообще сам думай! Ты ж программист в конце концов! – Євген Діулін Jan 20 '20 at 16:43