用JS / Coffeescript递归连接数组

时间:2011-08-15 12:43:45

标签: javascript recursion coffeescript

我正在尝试生成类似于以下内容的内容:

鉴于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。任何人都可以帮我弄清楚如何正确的for循环生成一个数组?这是a gist供参考。

5 个答案:

答案 0 :(得分:4)

一种有点惯用的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']

第一个splat(...)将嵌套推导创建的2D数组转换为concat()的1D数组参数列表。

这个解决方案显然效率不高,因为它只会丢弃任何不需要的所有排列。

答案 1 :(得分:0)

听起来你基本上想要一个双嵌套循环,每次迭代每个字符时迭代每个字符,在这种情况下你也可以使用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”]。

答案 2 :(得分:0)

您真正想要解决的问题是“如何生成集合的所有子集”,特别是“如何生成给定数组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);

答案 3 :(得分:0)

问题似乎确实是生成给定集合的可能子集的集合。有时被称为集合的幂集。

基本上有3种可能的解决方案: 1)二项式系数。见http://en.wikipedia.org/wiki/Binomial_coefficient   可以在Pyton的itertools中找到一个实现。二项式系数为您提供一定长度的子集。如果将长度为0的子集组合到原始集的长度,则完成。

2)一种递归算法,可以在几代中“增长”子集。见京都的答案。请参阅下面的更详细的版本。维基百科文章提到帕斯卡的三角形,这是对这种算法的暗示

3)元素是否在子集中。这意味着存在2 ^(集合的长度)子集。    每个子集可以被编码为具有子集数字长度的二进制数。    这是在NT3RP的答案中完成的。您也可以使用一个布尔数组而不是字符串。我在下面发布了我的C#版本。

基于Miranda中的实现,我在coffeescript中的Powerset递归版本。 (我想知道我是否可以在Coffeescript中将其编码为与Miranda一样紧凑,然后我发现了这个问题)

在米兰达的powerset

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

powerof in 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#实现 不久前的子集。为了好玩,我还想写一个“增长”子集的版本。

我知道Miranda是一种非常简洁的函数式编程语言。我想知道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;
}
...

答案 4 :(得分:0)

一个简单的解决方案:

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

这可以优化为不使用嵌套循环每次都查找基本字符,但为了清楚起见,保留原样。