Вот еще один способ использования генераторов.Преимущество этого подхода состоит в том, что комбинации генерируются лениво.В зависимости от ваших расчетов иногда вы можете получить желаемый результат, прежде чем генерировать все комбинации.Поскольку многие комбинаторные задачи включают в себя огромные наборы комбинаций, мы можем потенциально сэкономить много ресурсов от пропуска генерации ненужных комбинаций.Кроме того, эта концепция может быть расширена для работы с бесконечными потоками, где возможно получение бесконечного числа комбинаций.
const append = (xs, x) =>
xs .concat ([ x ])
const ncomb = function* (n, xs = [])
{ const gen = function* (n, acc)
{ if (n === 0)
yield acc
else
for (const x of xs)
yield* gen (n - 1, append (acc, x))
}
yield* gen (n, [])
}
const data =
[ 1, 2, 3 ]
const print = (...xs) =>
console .log (...xs .map (x => JSON .stringify (x)))
print
( Array .from (ncomb (0, data))
// [[]]
, Array .from (ncomb (1, data))
// [[1],[2],[3]]
, Array .from (ncomb (2, data))
// [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]]
, Array .from (ncomb (3, data))
// [[1,1,1],[1,1,2],[1,1,3],[1,2,1],[1,2,2],[1,2,3],[1,3,1],[1,3,2],[1,3,3],[2,1,1],[2,1,2],[2,1,3],[2,2,1],[2,2,2],[2,2,3],[2,3,1],[2,3,2],[2,3,3],[3,1,1],[3,1,2],[3,1,3],[3,2,1],[3,2,2],[3,2,3],[3,3,1],[3,3,2],[3,3,3]]
)
Выше комбинаций увеличивать крайний правый элемент, но этот порядок можно изменить.Если вы измените операцию append
на prepend
, вы сгенерируете комбинации, в которых вместо этого увеличивается левый элемент -
const prepend = (xs, x) =>
[ x ] .concat (xs)
print
( Array .from (ncomb (3, data))
// [[1,1,1],[2,1,1],[3,1,1],[1,2,1],[2,2,1],[3,2,1],[1,3,1],[2,3,1],[3,3,1],[1,1,2],[2,1,2],[3,1,2],[1,2,2],[2,2,2],[3,2,2],[1,3,2],[2,3,2],[3,3,2],[1,1,3],[2,1,3],[3,1,3],[1,2,3],[2,2,3],[3,2,3],[1,3,3],[2,3,3],[3,3,3]]
)
Чтобы продемонстрировать поведение генератора при коротком замыкании, рассмотрим функцию solve
, котораяпринимает любое количество чисел и возвращает первую пифагорейскую тройку .После того, как первый прямоугольный треугольник найден, дополнительные комбинации не будут сгенерированы -
const isRightTriangle = (a, b, c) =>
// pythagorean triple
(a ** 2) + (b ** 2) === (c ** 2)
const solve = (...numbers) =>
{ for (const [ a, b, c ] of ncomb (3, numbers))
if (isRightTriangle (a, b, c))
return `solution: ${a}, ${b}, ${c}`
return `no solution`
}
console .log (solve (1, 2, 3, 4, 5, 6, 7, 9, 10))
// solution: 3, 4, 5
console .log (solve (8, 11, 6, 9, 5, 10, 12, 7))
// solution: 8, 6, 10
console .log (solve (19, 6, 8, 7, 21, 4, 3, 15))
// no solution
Разверните фрагмент ниже, чтобы проверить результаты в своем собственном браузере -
const append = (xs, x) =>
xs .concat ([ x ])
const ncomb = function* (n, xs = [])
{ const gen = function* (n, acc)
{ if (n === 0)
yield acc
else
for (const x of xs)
yield* gen (n - 1, append (acc, x))
}
yield* gen (n, [])
}
const isTriangle = (a, b, c) =>
// pythagorean theorem
(a ** 2) + (b ** 2) === (c ** 2)
const solve = (...numbers) =>
{ for (const [ a, b, c ] of ncomb (3, numbers))
if (isTriangle (a, b, c))
return `solution: ${a}, ${b}, ${c}`
return `no solution`
}
console .log (solve (1, 2, 3, 4, 5, 6, 7, 9, 10))
// solution: 3, 4, 5
console .log (solve (8, 11, 6, 9, 5, 10, 12, 7))
// solution: 8, 6, 10
console .log (solve (19, 6, 8, 7, 21, 4, 3, 15))
// no solution