获取子数组的排列而无需更改子数组的顺序

时间:2018-09-28 01:56:44

标签: javascript arrays generator permutation

所以我的目标是将任意数字字符串作为一个数组,将它们排序为匹配字长的子数组,然后在不更改子数组顺序且遵循以下规则的情况下排列每个子数组:

  1. 如果任何组只有一个单词,则保持原样。 (即['cat'])
  2. 置换第一组直到完成。
  3. 将第二组置换一次,然后执行步骤1;每次执行步骤1时,仅对第二组进行置换。
  4. 将第三组置换一次,然后执行步骤2;每次完成步骤2时,仅置换第三组。
  5. 继续执行此模式,直到完成所有分组。

我几乎能够做到这一点,但是我一直未能使所有排列都出来。我一直在努力解决这一问题,所以我决定环顾四周,现在我来寻求帮助。到目前为止,这是我所得到的(我对奇怪的变量名提前表示歉意):

function* permute(permutation) {
  const length = permutation.length;
  let c = Array(length).fill(0),
    i = 1,
    k, p;

  yield permutation;
  while (i < length) {
    if (c[i] < i) {
      k = (i % 2) ? c[i] : 0;
      p = permutation[i];
      permutation[i] = permutation[k];
      permutation[k] = p;
      ++c[i];
      i = 1;
      yield permutation;
    } else {
      c[i] = 0;
      ++i;
    }
  }
}
function* permuteGroups(input) {
  input.sort((a,b) => {return a.length-b.length;});
  let groups = [], //Holds the list of Generators and words
      groupsCache = [], //Copy of groups to reset the Generators
      g = 0, i = input.length, len = input[i - 1].length,
      gid = 0, currentGroup, currentSet, finished = [];

  //Helper function to reset a Generator
  function reset(gid) {
    groups[gid] = {
      'currentValue': undefined,
      'nextValue': undefined,
      'generator': permute(groupsCache[gid].split(',')),
      'gen2': permute(groupsCache[gid].split(','))
    };
    groups[gid].nextValue = groups[gid].gen2.next().value.join(',');
  }
  function fetch(gid, next) {
    if (!!next) {
      if (groups[gid].generator === undefined) return { 'currentValue': groups[gid].currentValue, 'nextValue': undefined };
      let cv = groups[gid].generator.next();
      let nv = groups[gid].gen2.next();
      cv = (cv.done) ? 'done' : ((cv.value) ? cv.value.join(',') : undefined);
      nv = (nv.done) ? 'done' : ((nv.value) ? nv.value.join(',') : undefined);
      groups[gid].currentValue = cv;
      groups[gid].nextValue = nv;
    }
    let output = {
      'currentValue': groups[gid].currentValue,
      'nextValue': groups[gid].nextValue
    };
    return output;
  }
  function getOutput(next) {
    let gid = 0, len = groups.length, output = [];
    while (gid < len) {
      output.push(fetch(gid++, next).currentValue);
    }
    return output;
  }

  //First move words into sub-arrays based on length
  groups.push([]);
  while (i--) {
    if (input[i].length == len) {
      groups[g].push(input[i]);
      continue;
    }
    groups[++g] = [input[i]];
    len = input[i].length;
  }
  groupsCache = groups.map(x => x.join(',')); // Convert each group into a string to preserve order
  //Turn the sub-arrays into Generators if they have more than one item
  len = groups.length - 1;
  while (i++ < len) {
    groups[i] = (groups[i].length > 1) ? { 'currentValue': undefined, 'nextValue': undefined, 'generator': permute(groups[i].toString().split(',')), 'gen2': permute(groups[i].toString().split(',')) } : { 'currentValue': groups[i].toString().split(','), 'nextValue': undefined, 'generator': undefined, 'gen2': undefined };
    if (groups[i].gen2) groups[i].nextValue = groups[i].gen2.next().value.join(',');
    finished[i] = (groups[i].nextValue) ? false : true;
  }
  //console.log(finished)

  //Yield initial output
  yield getOutput(true);

  let index = 0, isNotDone = true, isInc = false;
  while (isNotDone) {
    // If we're supposed to be going up to the next group, but there isn't one, reset the index back to zero
    // Fetch the next value for the current group...
    currentSet = fetch(index, true);
    /* default, default, default
     / "rabbit,beaver,calf,wolf,cat,fox,dog"
     * Flip [0], default, default
     / "beaver,rabbit,calf,wolf,cat,fox,dog"
     * Reset [0], flip [1], default
     / "rabbit,beaver,wolf,calf,cat,fox,dog"
     * Flip [0], flipped, default
     / "beaver,rabbit,wolf,calf,cat,fox,dog"
     * Reset [0], Reset [1], move [3]
     / "rabbit,beaver,calf,wolf,fox,cat,dog"
    */
    // and yield it.
    yield getOutput();
    // If this was the last value before this group is done...
    if (currentSet.nextValue == 'done') {
//       if (!groups[index+1] || finished.slice(index+1).every(x => x === true)) {
//         finished[index] = true;
//         console.log(finished)
//         if (finished.every(x => x === true)) {
//           isNotDone = false;
//         }
//         index = 0;
//         continue;
//       }
      // reset the current group
      reset(index);
      // and put it back on the first value
      fetch(index, true);
      // Move on to the next group
      index++;
      // Mark that we plan on going to the next group up
      isInc = true;
      continue;
    }
    // Otherwise if it is not the last value and the isInc flag is set
    if (isInc) {
      // Reset the index back to zero
      index = 0;
      isInc = false;
    }
  }
  return 'done';
}
let perm = permuteGroups(['dog','fox','cat','wolf','calf','beaver','rabbit']);
let result = perm.next();
while (!result.done) {
  console.log(result.value.toString());
  result = perm.next();
}

1 个答案:

答案 0 :(得分:0)

休息片刻后,我设法使其按需工作。这是将来任何人的最终结果。可以按原样将其推送到Codepen中,然后开始工作,但是我建议使用较小的单词列表,否则在填满控制台后会挂起。我只剩下示例列表以显示它可以处理的内容。

我的第一种方法的主要问题是,当单个生成器运行正常时,如果我们需要提升到下一个组,则会试图提前处理。

function* permute(permutation) {
  const length = permutation.length;
  let c = Array(length).fill(0),
    i = 1, k;

  yield permutation;
  while (i < length) {
    if (c[i] < i) {
      k = (i % 2) ? c[i] : 0;
      [permutation[i], permutation[k]] = [permutation[k], permutation[i]];
      ++c[i];
      i = 1;
      yield permutation;
    } else {
      c[i] = 0;
      ++i;
    }
  }
}

function* permuteGroups(input) {
  input.sort((a,b) => {return a.length-b.length;});
  let groups = [], //Holds the list of Generators and words
      groupsCache = [], //Copy of groups to reset the Generators
      g = 0, i = input.length, len = input[i - 1].length,
      gid = 0, currentGroup, currentSet;

  //Helper function to reset a Generator
  function reset(gid) {
    groups[gid] = {
      'currentValue': undefined,
      'generator': permute(groupsCache[gid].split(','))
    };
  }
  function fetch(gid, next) {
    let group = groups[gid];

    // IF next and next === true
    if (!!next) {
      if (group.generator === undefined) return { 'currentValue': group.currentValue };

      let cv = group.generator.next();
      group.currentValue = (cv.done ? 'done' : (cv.value ? cv.value.join(',') : undefined));
    }

    return {
      'currentValue': group.currentValue
    };
  }
  function getOutput(next) {
    return groups.map((group, index) => fetch(index, next).currentValue);
  }

  //First move words into sub-arrays based on length
  groups.push([]);
  while (i--) {
    if (input[i].length == len) {
      groups[g].push(input[i]);
      continue;
    }
    groups[++g] = [input[i]];
    len = input[i].length;
  }
  groupsCache = groups.map(x => x.join(',')); // Convert each group into a string to preserve order
  //Turn the sub-arrays into Generators if they have more than one item
  groups = groups.map(group => (group.length > 1 ? { 'currentValue': undefined, 'generator': permute(group.toString().split(',')) } : { 'currentValue': group.toString().split(','), 'generator': undefined }) );
  //Yield initial output
  yield getOutput(true);

  let index = 0, isInc = false;
  while (true) {
    // If we're supposed to be going up to the next group, but there isn't one, reset the index back to zero
    // Fetch the next value for the current group...
    currentSet = fetch(index, true);
    // If this was the last value before this group is done...
    if (currentSet.currentValue == 'done' || Array.isArray(currentSet.currentValue)) {
      // reset the current group
      reset(index);
      // and put it back on the first value
      fetch(index, true);
      // Move on to the next group
      index++;
      if (index === groups.length) {
          // We're done!
          return;
      }
      // Mark that we plan on going to the next group up
      isInc = true;
      continue;
    }
    // and yield it.
    yield getOutput();
    // Otherwise if it is not the last value and the isInc flag is set
    if (isInc) {
      // Reset the index back to zero
      index = 0;
      isInc = false;
    }
  }
}
// "yourself","hero","entire","blank","peg","usage","tot","debate","volt","lie","almond","baseball","sell","youngster","use","atom","left","sit","even","input","only","freight"
let perm = permuteGroups(["yourself","hero","entire","blank","peg","usage","tot","debate","volt","lie","almond","baseball","sell","youngster","use","atom","left","sit","even","input","only","freight"]), i = 1;
let result = perm.next();
while (!result.done) {
  console.log(i++, result.value.toString());
  result = perm.next();
}