-2

In a puzzle book I found the following puzzle:

Puzzle

The caption is in Dutch, but I can shortly describe it:

  • There is a sequence of numbers in the range 0...6, which is not given
  • There are 6 mathematical statements (listed below) that are true
  • By using logic and crossing off certain numbers that you know are certainly not it, whomever plays the puzzle can find the sequence of numbers (the code).

For example, in the pictured case, we know that A+C=5, thus we can infer that both A and C can not be 6. By doing these repeatedly, you will find the sequence.

Now, my question is: How could I write an algorithm that generates these puzzle values? I've tried to come up with a random sequence, and describe it with 6 similar mathematical statements, but in some cases the puzzle will be unsolvable.

Such an unsolvable case / statements are for example when the sequence is 012345, with statements A+B=1 B+C=3 C+D=5 D+E=7 E+F=9 F+A=5

knart
  • 311
  • 2
  • 13
  • 2
    It is not unsolvable if you need to make a guess, you sometimes need to *branch* to find the solution (so fill in a value, and reason further, and if that is a dead end remove the value and try again). I think more important is to verify with the given rules that there is one, and *only* one solution. – Willem Van Onsem Apr 28 '20 at 22:15
  • Can the sequence have duplicate numbers or must they all be unique? – גלעד ברקן May 01 '20 at 11:56

3 Answers3

4

First, let's elaborate a solution S for this problem:

For each variable, create a set {0,1,2,3,4,5,6} with its possible values.

Analyse each equation and cut values from the variables' sets based on the equations.

example: A + B = 3 (we can cut 4, 5 and 6 from A and B sets).

When all sets have only 1 element, that's your answer.

On the other hand, if all equations are analysed and no set changes, it's impossible.

Example:  (1) A + C = 5       (2) B - D = 5       (3) B + C = 6
          (4) E * F = 6       (5) B - E = 3       (6) A + D = 6

Analysing (1): Reduce A and C to {0,1,2,3,4,5}.
Analysing (2): Reduce B to {5,6}. Reduce D to {0,1}.
Analysing (3): Reduce C to {0,1}
Analysing (4): Reduce E and F to {1,2,3,6}.
Analysing (5): Can't reduce.
Analysing (6): Reduce A to {5}. Reduce D to {1}.
--------------------------------------------------
Not over yet, but something changed. Restart:
--------------------------------------------------
Analysing (1): Reduce C to {0}
Analysing (2): Reduce B to {6}.
Analysing (3): Can't reduce.
Analysing (4): Reduce E and F to {1,2,3,6}.
Analysing (5): Reduce E to {3}.
Analysing (6): Can't reduce.
--------------------------------------------------
Not over yet, but something changed. Restart:
--------------------------------------------------
Analysing (1): Can't reduce.
Analysing (2): Can't reduce.
Analysing (3): Can't reduce.
Analysing (4): Reduce F to {2}.
Analysing (5): Can't reduce.
Analysing (6): Can't reduce. 
--------------------------------------------------
Over. All sets of size 1. Answer: (5,6,0,1,3,2)
--------------------------------------------------  

Now, knowing how to solve, I would create puzzles the following way:

Choose a random answer.

example: (4, 1, 3, 2, 0, 5).

Generate 6 random equations with it.

Run S with this equations.

If S ends, you have a possible group of equations.

Otherwise, restart.

Community
  • 1
  • 1
Daniel
  • 7,357
  • 7
  • 32
  • 84
2

There are 7^6 = 117649 possible codes. With 6 questions, we only need each to remove 6/7 of the search space. If the first few do more, then so much the better.

For the three questions, it is easy. When you look at the distribution of ways to get a sum or a difference you find that there are can be no more than 7/49 = 49 ways to get an answer and usually less. So partition A, B, C, D, E, F into 3 pairs of 2, supply random operations, and you have no more than 343 possible answers left.

With your example, I randomly generated D-A=4, C+F=7, D-B=2. For the first pair we have 3 possibilities, for the second we have 6, for the third we have 5.

Now what we need to do is actually construct all of the possibilities (in this case there are 90 of them). And then you can construct random statements, and only accept ones that are on the path to 1 solution. Which means that with 3 questions left, you cut down by a factor of at least the cube-root (in this case leaving no more than 20 possibilities), with 2 by the square root (leaving no more than 4), and then a single possibility.

Given that a random question on average cuts your search space by at least 6/7, random questions are likely to do this pretty easily. (The challenge is in verifying the answer.)

btilly
  • 43,296
  • 3
  • 59
  • 88
0

This seems like nothing more than simple algebraic substitutions. Moreover, the choice of operations in the equations does not matter. From

(D, B) (B, C)

we get

(D, C)

then from

(D, C) (C, A)

we get

(D, A)

An equation for (A, D) already exists so we can solve for both. From there, it's easy to see how to complete the solution.

In order to create a puzzle, start with any such double equation, say

(E, F) (E, F)

then split one of them into the more obscure, like

(E, B) (B, F)

that gives us three of the six equations (remember the choice of operations can be purely random). I leave the reader to give more thought to completion.

I wouldn't expect completion and implementation to be trivial but hopefully this can give us an idea of how this can be done quite deliberately without backtracking.

JavaScript code:

const vars = ["A", "B", "C", "D", "E", "F"];

function getOps(pair){
  return pair.includes(0) ? ["+", "-"] : ["+", "-", "*"];
}

function getRandom(k, list){
  let result = [];
  for (let i=0; i<k; i++)
    result = result.concat(
      list.splice(~~(Math.random()*list.length), 1));
  return result;
}

function getNum(a, b, op){
  return eval(`${ a } ${ op } ${ b }`);
}

function getEquation(a, b, op, idxs){
  // Randomise the order of the variables
  if (Math.random() > 0.5)
    [a, b] = [b, a];
  return `${ vars[idxs[a]] } ${ op } ${ vars[idxs[b]] } = ${ getNum(a, b, op) }`;
}

function f(){
  let remaining = [0, 1, 2, 3, 4, 5, 6];
  let result = [];
  // Remove one entry
  remaining.splice(~~(Math.random()*7), 1);
  // Shuffle
  remaining = getRandom(6, remaining);
  const idxs = Object.fromEntries(
    remaining.map((x, i) => [x, i]));
 
  // Select equation pairs
  const [a, b] = getRandom(2, remaining);
  const [c, d] = getRandom(2, remaining);
 
  result.push(
    getEquation(a, c, getRandom(1, getOps([a, c])), idxs),
    getEquation(a, d, getRandom(1, getOps([a, d])), idxs),
    getEquation(b, c, getRandom(1, getOps([b, c])), idxs),
    getEquation(b, d, getRandom(1, getOps([b, d])), idxs)
  );

  const [e] = getRandom(1, remaining);
  const [f] = remaining;
  const [aa] = getRandom(1, [a, b, c, d]);

  result.push(
    getEquation(e, f, getRandom(1, getOps([e, f])), idxs),
    getEquation(e, aa, getRandom(1, getOps([e, aa])), idxs)
  );

  const legend = Object.entries(idxs)
    .map(([x, i]) => [vars[i], Number(x)])
    .map(([a, b]) => `(${ a } ${ b })`)
    .sort()
    .join(' ');

  return {
    equations: getRandom(6, result).join('\n'),
    legend: legend
  };
}

var result = f();

console.log(result.equations);
console.log('');
console.log(result.legend);
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61