0

How can i combine the arrays?

[
  ['1','1','1','2','1'],
  ['3','3','4','4','4'],
  ['6','6','7','8','7'], 
]

with those arrays there are many combinations, some of the are these:

1,3,6
1,3,6 -> this not, because its duplicate
1,4,7
2,4,8
1,4,7 -> this not, because its duplicate

how can we replace the wrong combination? one solution will be:

1,3,6
1,3,7 -> fixed
1,4,6
1,4,8
2,4,7 -> fixed

rules:

in the first column, 1 should appear 4 times
in the first column, 2 should appear 1 times

in the second column, 3 should appear 2 times
in the second column, 4 should appear 3 times

in the third column, 6 should appear 2 times
in the third column, 7 should appear 2 times
in the third column, 8 should appear 1 times

my attempt is: i would like to generate many combinations, but its not safe the number of appears

function available(array) {
  let currentIndex = array.length,
    randomIndex;

  while (currentIndex != 0) {

    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex--;

    [array[currentIndex], array[randomIndex]] = [
      array[randomIndex], array[currentIndex]
    ];
  }
  return array;
}

const arrays = [
  ['1', '1', '1', '2', '1'],
  ['3', '3', '4', '4', '4'],
  ['6', '6', '7', '8', '7'],
]
for (const current of arrays) {
  console.log(available(current))
}
signup
  • 135
  • 1
  • 16
  • Please read [ask] and [mcve] before posting a question. – zer00ne Jan 28 '22 at 04:03
  • @zer00ne i need you help to create an algorithm please, i dont need the code – signup Jan 28 '22 at 04:07
  • If you have JavaScript that you are having trouble with, I will help you. I'll be happy to assist you with algorithms if you post it in JavaScript. – zer00ne Jan 28 '22 at 04:12
  • On a breif review of the pattern you want...it doesn't make sense -- explain why the duplicates are changed in a random manner? `[1,3,6]` to `[1,3,7]` and `[1,4,8]` to `[2,4,7]` – zer00ne Jan 28 '22 at 04:19
  • Brute force it. There's only `5!^3 = 1728000` combinations – Ouroborus Jan 28 '22 at 04:20
  • @zer00ne It can be any combination, you just have to respect the number of times of repetition in its respective column – signup Jan 28 '22 at 04:29

2 Answers2

2

Ignore that the individual strings contain digits. This doesn't matter. At that level, all that matters is that they're strings. (We wouldn't care about that either, just that they're some kind of comparable symbol, but we can take advantage of the fact that they're stings in validation.)

Integers, in general, don't have to have any particular representation. Just because, say, 3 is shaped like that doesn't say anything about the concept of 3. It could be represented in any way so long as it's unique relative to others of its type. For example, we could represent it as [1,1,1].

For each row, we need to iterate through the permutations of that row. Heap's Algorithm allows us to do that in a predictable way without breaking the rules for how many of each kind of item goes in each row.

If we think of that iteration as a kind of counting (the first permutation is 1, the next is 2, etc.), we can count through the permutations of each row as though they were digits in a strange kind of integer. In this way, we can iterate over all combinations of the permutations of the rows.

All that's left is validating each result against duplicate columns. We do this by joining along columns, and adding the results to a set. So long as the set ends up with the same length as the rows, we know there were no duplicates.

solve() only returns the first solution encountered, if any. However, it would be pretty simple to adjust it to return all solutions.

We use .every as a kind of .forEach with the additional ability to end the loop early and to signal whether or not that happened.

function* is known as a generator function. They return a generator object, a thing that wraps the function and lets you return values (via yield) and then later come back and resume the function from that point.

// Heap's Algorithm modified to skip swaps of the same value
// Allows us to iterate over permutations of a row
function* iterHeapsAlgo(arr) {
  const A = Array.from(arr); // shallow copy
  const len = A.length;
  const c = new Array(len).fill(0);
  let i = 0;
  yield A;
  while(i < len) {
    if(c[i] < i) {
      let j = i&1 ? c[i] : 0;
      if(A[i] != A[j]) {
        [A[j], A[i]] = [A[i], A[j]];
        yield A;
      }
      c[i]++;
      i = 0;
    } else {
      c[i] = 0;
      i++;
    }
  }
};

// Iterate over all combinations of all rows
// Typical counting algorithm with shortcut to take advantage of exposed state
function* iterCount(data) {
  const state = data.map(v => iterHeapsAlgo(v));
  const current = state.map(v => v.next().value);
  yield current;
  while(true) {
    const isEnd = state.every((v,i) => {
      let n = v.next();
      if(n.done) {
        state[i] = iterHeapsAlgo(data[i]);
        current[i] = state[i].next().value;
        return true;
      }
    });
    if(isEnd) return;
    yield current;
  }
}

const validate = data => {
  const set = new Set();
  return data[0].every((_,i) => {
    set.add(data.reduce((s,v) => `${s}\uffff${v[i]}`, ''));
    return set.size-1 == i;
  });
};

const solve = start => {
  for(const current of iterCount(start)) {
    if(validate(current)) {
      return current.map(v => Array.from(v)); // depth 2 copy
    }
  }
  return null;
};

console.log(solve([
  ['1','1','1','2','1'],
  ['3','3','4','4','4'],
  ['6','6','7','8','7'],
]) || 'No solution found');
.as-console-wrapper { top: 0; max-height: 100% !important; }

EDIT: Adjustments based on comments. Thanks @Mulan and @ScottSauyet

Ouroborus
  • 16,237
  • 4
  • 39
  • 62
  • you can use `for..of` to step thru an iterator. ie,`function solve(start) { for (const state of iterCount(start)) if (validate(state)) return state.map(v => Array.from(v)); return null }`. touching `iterable.next()` is almost always unnecessary. – Mulan Jan 28 '22 at 08:36
  • aside from that, the computational space/time complexity of this algorithm is staggering – Mulan Jan 28 '22 at 08:51
  • @Mulan Thanks for pointing out the simplification for the `solve` loop. I think space/time complexity here is `O(n)` and `O(n!)`, as good as it can get for brute force. – Ouroborus Jan 28 '22 at 18:23
  • Without looking through the code, I can tell that there's something wrong, as your output seems to just be a copy of the input ... when the request is for a number of length-3 arrays. – Scott Sauyet Jan 28 '22 at 21:23
  • Oh, I see. Your answer is a transposition of what I would expect. You have `11121/43344/66787` where I would expect `146/136/137/248/147`. I'll have to dig into the code to understand it. – Scott Sauyet Jan 28 '22 at 21:33
  • 1
    @ScottSauyet This solution produces output similar to that of the question's snippet. – Ouroborus Jan 28 '22 at 21:40
  • Ahh, that's what I get for reading answers first! That's an odd notion of 'columns', but I can live with it. – Scott Sauyet Jan 28 '22 at 21:43
  • Looking through now, one issue in `validate`: you can't just concatenate the numbers to see if you have a duplicate. It works if these are all single-digit, but then falls down. If the first list contained `'[1', '11']` and the second one `['2', '12']`, then you would get keys [`'12', '112', '112', and '1112']`, and although there are four unique pairs, you have a duplicated key. `s.add(data.join('~'))` might be fine, depending upon the data. – Scott Sauyet Jan 28 '22 at 21:43
  • @ScottSauyet Good catch, I did make the assumption that the initial strings would be single characters. – Ouroborus Jan 28 '22 at 21:48
  • 1
    For much more complex, but more efficient, permutation of multisets, you might look at https://github.com/ekg/multipermute. – Scott Sauyet Jan 28 '22 at 21:56
  • @Ouroborus its necesary use Generator function?, or is possible do without it? – signup Jan 31 '22 at 14:50
  • @signup Generator functions in javascript are largely syntactical sugar. You can write a generator without using `function*` but you sacrifice readability. You can also combine all of the above functions into one monolithic function. The generators become loops. However, this means you're also mixing several algorithms together so the results is far less readable. You could also convert the generators to just output their entire array. This doesn't change readability but does increase space and time requirements. – Ouroborus Jan 31 '22 at 17:43
  • @Ouroborus will it work with arrays with a size of 15000 elements? – signup Jan 31 '22 at 21:13
  • @signup Yes. It does most stuff in place so memory shouldn't be a constraint. No idea how long it would take to process something that large. `O(n!)` algorithms aren't particularly speedy for large sets. On the other hand, if a solution is found early, the rest of the processing doesn't happen. – Ouroborus Jan 31 '22 at 21:22
  • @signup Ha! Yeah, you'll likely need an algorithm with some heuristics. I imagine this problem is from one of those coding puzzle sites. If it's part of a series, you might get clues from how the easier puzzles in the series are solved. – Ouroborus Jan 31 '22 at 22:00
  • @Ouroborus can you help me please with this https://stackoverflow.com/questions/70934619/how-can-i-generate-combinations-randonly-respecting-rules – signup Feb 01 '22 at 01:14
2

Maybe I'm missing something, but it seems like you're looking for the product of the arrays with only unique combinations of elements -

const unique = a =>
  Array.from(new Set(a))
  
const product = t =>
  t.length == 0
    ? [[]]
    : product(t.slice(1)).flatMap(p => unique(t[0]).map(v => [v, ...p]))
    
const myinput = [
  ['1','1','1','2','1'],
  ['3','3','4','4','4'],
  ['6','6','7','8','7'], 
]

for (const combination of product(myinput))
  console.log(String(combination))
1,3,6
2,3,6
1,4,6
2,4,6
1,3,7
2,3,7
1,4,7
2,4,7
1,3,8
2,3,8
1,4,8
2,4,8

By inverting the loops, we can generate the unique products in lexicographical order -

const bind = (f, x) => f(x)

const unique = a =>
  Array.from(new Set(a))

const product = t =>
  t.length == 0
    ? [[]]
    : bind
        ( r => unique(t[0]).flatMap(v => r.map(p => [v, ...p]))
        , product(t.slice(1))
        )
    
const myinput = [
  ['1','1','1','2','1'],
  ['3','3','4','4','4'],
  ['6','6','7','8','7'], 
]

for (const combination of product(myinput))
  console.log(String(combination))
1,3,6
1,3,7
1,3,8
1,4,6
1,4,7
1,4,8
2,3,6
2,3,7
2,3,8
2,4,6
2,4,7
2,4,8

Finally we can generate the products lazily by using a generator -

const unique = a =>
  Array.from(new Set(a))
       
function* product(t) {
  if (t.length == 0)
    yield []
  else
    for (const p of product(t.slice(1)))
      for (const v of unique(t[0]))
        yield [v, ...p]
}

const myinput = [
  ['1','1','1','2','1'],
  ['3','3','4','4','4'],
  ['6','6','7','8','7'], 
]

for (const combination of product(myinput))
  console.log(String(combination))
1,3,6
2,3,6
1,4,6
2,4,6
1,3,7
2,3,7
1,4,7
2,4,7
1,3,8
2,3,8
1,4,8
2,4,8

If you are looking for permutations, see this related Q&A.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • You list the permutations, but you still need to figure out a subset of those permutations for which all elements of the input are used exactly once. – Ouroborus Jan 28 '22 at 17:57
  • @Ouroborus: This already removes duplicates. Look at the call to `unique` inside each function. – Scott Sauyet Jan 28 '22 at 21:08
  • @Mulan: I don't think this is asking for the full cartesian product. Those "rules" makes me think that the desired answer would have only five entries, satisfied, for example, by `[[1, 3, 6], [1, 3, 7], [1, 4, 6], [1, 4, 7], [2, 4, 8]]`. No thought on an actual algorithm yet, though. – Scott Sauyet Jan 28 '22 at 21:16
  • 1
    @ScottSauyet That's the interpretation I had as well. I was trying to say that in my earlier comment but apparently failed. – Ouroborus Jan 28 '22 at 21:22
  • the post is written _"with those arrays there are many combinations, some of the are these..."_ where i understood the examples provided were not a complete answer. other parts of the question are poorly constructed. if the OP can clarify i am happy to update this post. – Mulan Jan 31 '22 at 14:17
  • @Ouroborus _"figure out a subset of those permutations for which all elements of the input are used exactly once"_ this wasn't my understanding either. the post provides many examples in the _"rules"_ section, _"...X should appear N times..."_. this answer provides distinct 3-tuples – Mulan Jan 31 '22 at 14:18
  • @Mulan any combination is ok if X appear N times – signup Jan 31 '22 at 15:36