简体   繁体   English

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

[英]Get permutations of sub-arrays without changing order of sub-arrays

So my goal is to take in an arbitrary number strings as an array, sort them into sub-arrays of matching word-length, then permute each sub-array without changing the order of the sub-arrays and following these rules: 因此,我的目标是将任意数字字符串作为数组,将它们排序为匹配字长的子数组,然后在不更改子数组顺序且遵循以下规则的情况下排列每个子数组:

  1. If any group has only one word, it is left as-is. 如果任何组只有一个单词,则保持原样。 (ie ['cat']) (即['cat'])
  2. Permute the first group until done. 排列第一组直到完成。
  3. Permute the second group once, then do step 1; 将第二组置换一次,然后执行步骤1; only permuting the second group once each time step 1 is done. 每次执行步骤1时仅对第二组进行置换。
  4. Permute the third group once, then do step 2; 将第三组置换一次,然后执行步骤2; only permuting the third group once each time step 2 is done. 每次执行步骤2时仅对第三组进行置换。
  5. Continue this pattern until all groups are done. 继续此模式,直到完成所有组。

I've nearly been able to accomplish this, but I keep failing to get all permutations to come out. 我几乎能够做到这一点,但是我一直未能使所有排列都出来。 I've been struggling to figure it out for a while now, so I decided to look around a LOT and now I've come to ask for help. 我一直在努力解决这一问题,所以我决定环顾四周,现在我来寻求帮助。 Here's what I've got so far (I apologize in advance for the strange variable names): 到目前为止,这是我所得到的(对于那些奇怪的变量名,我事先表示歉意):

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();
}

After taking a break from it for a while, I managed to get it to work as desired. 稍作休息后,我设法使其按需工作。 Here's the end result for anyone in the future. 这是将来任何人的最终结果。 It can be shoved into a Codepen as-is and will start working, but I suggest using a smaller list of words or it'll hang after a good while of filling up your console. 可以按原样将其推送到Codepen中,然后开始工作,但是我建议使用较小的单词列表,否则在填满控制台后会挂起。 I'm just leaving my example list to show what it can handle. 我只剩下示例列表以显示它可以处理的内容。

The main problem with my first approach was trying to look ahead to process if we need to step up to the next group, when a single generator worked just fine. 我的第一种方法的主要问题是,如果我们需要升级到下一个小组,那么当单个生成器运行良好时,尝试提前处理。

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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM