Рекурсивно объединенный массив с JS / Coffeescript - PullRequest
1 голос
/ 15 августа 2011

Я пытаюсь сгенерировать что-то похожее на следующее:

Учитывая HINTS = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"], тогда allHints(21) должен дать массив, похожий на:

["a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "aa", "as", "ad", "af", "ag", "ah", "aj", "ak", "af"..."sa", "ss", "sd"...]

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

Мое решение состояло в том, чтобы вложить два цикла for, повторяющихся в каждой комбинации по 9 (учитывая, сколько там подсказок), но кажется, что она застревает где-то во второй раз.Я не слишком знаком с синтаксисом Coffeescripts for, но я пробовал что-то похожее на:

allHints = ->
  for i in [0...anchors]
    do (i) ->
      if i > 9
        (for y in [0...i % 9]
          do (y) ->
                       #changing this to HINTS[Math.round(i / 10)] as it should be produces weird results
            HINTS[y] + HINTS[i % 9 - 1])[0]
      else
        HINTS[i]


console.log allHints 19

Но, к сожалению, это обеспечивает undefined для последнего элемента.Может ли кто-нибудь помочь мне выяснить, как исправить для цикла для создания массива?Вот суть для справки.

Ответы [ 5 ]

4 голосов
/ 15 августа 2011

Несколько идиоматическое решение CoffeeScript:

HINTS = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"]

allHints = (n) ->
  HINTS.concat(((b + a for a in HINTS) for b in HINTS)...)[0...n]

Затем allHints(12) возвращает ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 'aa', 'as'].

Первый знак (...) преобразует двумерный массив, созданный вложенными представлениямив список аргументов массива 1D для concat().

Это решение, очевидно, не очень эффективно, поскольку оно генерирует все перестановки только для того, чтобы отбросить все, что ему не нужно.

0 голосов
/ 22 августа 2011

Простое решение:

allHints = (n) ->
    ln = hints.length

    # return sliced array if n is less than it's length
    return hints.slice(0, n) if n <= ln

    # clone the array, find number of possible combinations
    clone = hints.slice()
    limit = Math.min(n, ln*ln)

    # add the combinations up to the requested length or limit
    for i in [ln...limit]
        clone[i] = hints[Math.floor(i / ln) - 1] + hints[i % ln]

    return clone

Это может быть оптимизировано, чтобы не искать базовый символ каждый раз с помощью вложенного цикла, но для ясности оставлено как есть.

0 голосов
/ 20 августа 2011

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

В основном есть 3 возможных решения: 1) Биномиальные коэффициенты. Смотри http://en.wikipedia.org/wiki/Binomial_coefficient Реализация может быть найдена в itertools Pyton. Биномиальные коэффициенты дают вам подмножества определенной длины. Если вы объедините подмножества от длины 0 до длины вашего исходного набора, все готово.

2) Рекурсивный алгоритм, который "выращивает" подмножества в поколениях. Смотрите ответ Киото. См. Мою более подробную версию ниже. В статье в Википедии упоминается треугольник Паскаля, который является подсказкой для такого алгоритма

3) Элемент находится в подмножестве или нет. Это означает, что есть 2 ^ (длина набора) подмножеств. Каждое подмножество может быть закодировано как двоичное число с длиной цифр подмножеств. это сделано в ответе NT3RP. Вы также можете использовать массив логических значений для этого вместо строки. Я публикую свою версию C # ниже.

Моя рекурсивная версия Powerset в coffeescript, основанная на реализации в Miranda. (Мне было интересно, смогу ли я написать его в Coffeescript так же компактно, как в Miranda, и тогда я нашел этот вопрос)

powerset в Миранде

powerset []     = [[]]
powerset (x:xs) = [[x] ++ y | y <- ys] ++ ys
              where ys = powerset xs

powerset в coffeescript:

powerset = (zs) ->
  if zs.length is 0
    [[]]
else  
    [x,xs...]=zs
    ys=powerset xs
    result=([x].concat y for y in ys).concat ys

# number of elements in powerset is 2^length of the powerset

res=powerset [1,2,3,4,5,6,7,8,9,10]
console.log res
console.log "length:" +res.length

Мои интересы во всем этом:

Я написал C # реализацию подхода двоичных чисел для генерации подмножества некоторое время назад. Ради интереса я также хотел написать версию, которая "увеличивает" подмножества.

Я знал Миранду очень лаконичным языком функционального программирования. Я задавался вопросом, допускает ли coffeescript тот же уровень, что и succinctnes. Я не смог достичь этого в Scala, F # или Clojure. Я не смог сделать это в coffeescript, но «Киото» показал, как это делается.

Ниже версии C # как IEnumerable. Он генерирует кортежи элементов, которые находятся в подмножестве, и все остальные элементы.

...
//imports and house keeping removed
    private static IEnumerable<Tuple<IList<T>,IList<T>>> SubsetImpl<T>(this IList<T> argList){
 int elementCount=argList.Count; 
 int bits=(1<<elementCount);//2 to the power of elementCount
 List<Tuple<IList<T>,IList<T>>> subsets=new List<Tuple<IList<T>, IList<T>>>(bits);
 for(int i=0;i<bits;i++){
   int[] mask={i};          
   BitArray flags=new BitArray(mask);
    IList<T> incomb=new List<T>();
    IList<T> outcomb=new List<T>();     


   for(int j=0;j<argList.Count;j++){
    if( flags[j]){
      incomb.Add(argList[j]);               
    }else{
     outcomb.Add(argList[j]);
    }
   }
   Tuple<IList<T>,IList<T>> t=new Tuple<IList<T>,IList<T>>(incomb,outcomb);
   subsets.Add(t);
 }
 return subsets;
}
...
0 голосов
/ 15 августа 2011

Проблема, которую вы действительно хотите решить, заключается в том, «как мне сгенерировать все подмножества набора», в частности, «как сгенерировать все подмножества, используя массив HINTS».

Самый простой способ сделать это - учесть, что каждый элемент в вашем наборе может быть сопоставлен с двоичной строкой.Ваш массив, HINTS, имеет 10 элементов, поэтому для нахождения всех подмножеств нам просто нужно посчитать от 0000000000 до 1111111111 в двоичном виде, а затем выбрать любой элемент по ходу (например, 0000000101 будет "k;").

Код ниже почти завершен;Я посмотрел на него, и, кажется, есть ошибка (я посмотрю, смогу ли я отладить его позже).

HINTS = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"]

var results = [];
var binCounter;
var hint;
for ( var i = 0; i < Math.pow(2, HINTS.length); i++) {
    binCounter = i.toString(2); //convert the number to binary
    hint = "";

    //generate the hint
    for ( var j = 0; j < binCounter.length; j++) { 

        //if the boolean digit was true, pick the corresponding element from our set
        if (binCounter[j] == 1) {
            hint += HINTS[j];
        }
    } 
    results.push(hint);
}
console.log(results);
0 голосов
/ 15 августа 2011

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

Как это?

var HINTS = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"]

function allhints() {
  var all = Array(); // Create a new array
  all = all.concat(HINTS); // Add the individual hints
  for (var i=0; i<HINTS.length; i++) // for each character in HINTS
    for (var j=0; j<HINTS.length; j++) // with each character in HINTS
      all.push(HINTS[i] + HINTS[j]); // Append the two combined hint characters
  return all;
}

Это не рекурсивно, но рекурсия здесь не очень помогает. Это также дает желаемый результат, который вы описали, но без каких-либо ограничений длины. Если вы хотите ограничить результат, вы можете определить все подсказки следующим образом:

function allhints(n) {
  var all = Array(); // Create a new array
  all = all.concat(HINTS); // Add the individual hints
  for (var i=0; i<HINTS.length; i++) // for each character in HINTS
    for (var j=0; j<HINTS.length && all.length < n; j++)  // with each character in HINTS
      all.push(HINTS[i] + HINTS[j]); // Append the two combined hint characters
  return all.slice(0,n);
}

Итак, allhints (4) просто возвращает ["a", "s", "d", "f"].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...