0

Given a collection of one or more integer arrays of equal length, I'm looking to predict the most likely next array. Elements typically only increment by one or jump back to zero, though other changes are definitely possible.

Example 1:

[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
I'd expect to get:
[0, 0, 3]

Example 2:

[2, 0, 0]
[4, 1, 0]
[6, 2, 0]
I'd expect to get:
[8, 3, 0]

Example 3:

[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 1, 0]
[0, 1, 1]
[0, 1, 2]
I'd expect to get:
[0, 2, 0]

Cases 1 and 2 are easy enough to spot, but I'm having a hard time trying to figure out how to detect the pattern in example 3. What sort of keywords do I need to google to make some headway here?

Edit: response to Paul. Although each element in the pattern might look like anything, if the pattern isn't somehow build up from constant additions and cyclical resets to zero then the pattern is already so nonsensical that my algorithm doesn't have to do a good job any more. So I don't care about complicated polynomials or [+1, +1, +2, -5, +7] addition rules.

2
  • could you be a bit more specific about what the relationships might look like? is multiplication/division aswell possible, or simply addition, etc. Commented Sep 19, 2015 at 12:00
  • The pattern in example 3 is counting in base 3 arithmetic. Commented Sep 19, 2015 at 13:32

2 Answers 2

1

So if I get this right, in a given input you have

  • constant number(as in example 1, the column 1)
  • constant addition(as in example 1, the column 3)
  • cyclical(example 3 column 3)

I guess it wont be wrong to think that any of the two, or the three of them to be combined together(as in example 3, the column 2 is a combination of constant number and constant addition).

Firstly, we must consider that for any given input, we must take in account that all the cases can happen to one column. You can either make an object for any of those cases, a struct, or even none of them, by using different variables.

Secondly, you have to check each column. So while the column has not been checked completely, we look for multiple things:

  • is there a constant number? (two consecutive rows have the same number). If it's true, we remember in a variable the constant and the last row.

  • is there a difference between two consecutive numbers? If yes, then we have constant addition, and we remember in a variable the constant number that it's being used in the addition and where the addition happen. ATTENTION! We have to keep in mind that the addition can happen after a 3 constant numbers. So if we had a constant number and constant addition, we have to see where the constant number stopped so that we can continue it after the addition.

  • is there any cycle? Is the first number equal to any other number from the same column but any other row? If this happen, we just have to remember after how many addition(AND/OR Constant number) the cycle begins. This is the most tricky one: we can have constant number, constant addition and cycle. But it's not hard either, because previously we had constant number and constant addition. So, a cycle is simply those two repeating themselves.

This is the algorithm for finding simple patterns, using the information you gave us. I hope it's usefull.

Sign up to request clarification or add additional context in comments.

3 Comments

how would you go about detecting a cycle like 0,1,2,0,1,0,1,2,0,1,0,1,2,0,1,... ? You're right that it is the cyclical properties that are the hardest.
Ok. So the cycle here is 0, 1, 2, 0 ,1. First, we do as I said. We check if any number is equal to the first one. Check, the forth number is the same as the first one(the first three element). So, initially, we can assume that this cycle will go on forever. BUT, we can see that the sixth element is breaking the cycle, so now, we have to assume that the cycle is longer that 3 element. Now, we can put the number of element in the cycle to 5, because in the 6th it was broken. And we can see that the 5 elements will repeat themselves.
@DavidRutten To resume: if we have a cycle, and an element breaks the cycle, we increase the number of elements of the cycle and we go on like that, until the cycle is not broken anymore and we have the exact number of elements and numbers of the cycle.
0

Using Georgian's basic idea I gave up on trying to get this done in 20 lines or less. The code below certainly doesn't detect all patterns, but it's good enough for my purposes.

It will be able to recognise patterns that consist of repeating digits or repeating increments: enter image description here

Though it will fail to detect second order patterns such as: 0,1,1,2,2,2,3,3,3,3,4,4,4,4,4,...

/// <summary>
/// Create a difference array. This array contains all the values that need to be added to 
/// the elements in the original array in order to yield the next element. 
/// The difference array will be one element shorter.
/// </summary>
/// <param name="array">Array to operate on.</param>
/// <returns>Difference array.</returns>
public static int[] ToDifferenceArray(this int[] array)
{
  if (array == null || array.Length == 0)
    throw new ArgumentNullException("array");
  if (array.Length == 1)
    return new int[0];

  int[] div = new int[array.Length - 1];
  for (int i = 0; i < array.Length - 1; i++)
    div[i] = array[i + 1] - array[i];

  return div;
}
/// <summary>
/// Determine whether an array of integers contains repeating elements.
/// the last iteration of the cycle may be incomplete.
/// </summary>
/// <param name="array">Array to examine.</param>
/// <returns>Length of cycle or 0 if no cycle could be found.</returns>
public static int FindCycle(this int[] array)
{
  return FindCycle(array, 0);
}
/// <summary>
/// Determine whether an array of integers contains repeating elements.
/// the last iteration of the cycle may be incomplete.
/// </summary>
/// <param name="array">Array to examine.</param>
/// <param name="start">Index for cycle search.</param>
/// <returns>Length of cycle or 0 if no cycle could be found.</returns>
public static int FindCycle(this int[] array, int start)
{
  if (array == null || array.Length == 0)
    throw new ArgumentNullException("array");
  if (start < 0)
    throw new IndexOutOfRangeException("Search start may not be a negative number.");
  if (start >= array.Length)
    throw new IndexOutOfRangeException("Search start may not be larger than or equal to the length of the array.");

  int index0 = start;
  int index1 = index0;
  while (true)
  {
    // Find next occurrence of pattern start value.
    index1 = array.FirstIndexOf(array[index0], index1 + 1);
    if (index1 < 0)
      return 0;

    // Length of potential cycle.
    int length = (index1 - index0);

    // Test each remaining element of the array to see 
    // whether it indeed repeats at regular intervals.
    bool validCycle = true;
    for (int i = index1 + 1; i < array.Length; i++)
      if (array[i] != array[i - length])
      {
        validCycle = false;
        break;
      }
    if (validCycle)
      return length;
  }
}
/// <summary>
/// Attempt to continue an array of integers.
/// </summary>
/// <param name="array">Sequence to extend.</param>
/// <returns>Best guess for next number in sequence.</returns>
public static int ExtendSequence(this int[] array)
{
  // Handle special cases.
  // Empty pattern.
  if (array == null || array.Length == 0)
    return 0;

  // Pattern containing one element.
  if (array.Length == 1)
    return array[0];

  // Pattern containing only identical elements (very common case).
  bool constantPattern = true;
  for (int i = 1; i < array.Length; i++)
    if (array[0] != array[i])
    {
      constantPattern = false;
      break;
    }
  if (constantPattern)
    return array[0];

  // Pattern containing only constantly incrementing elements (very common case).
  constantPattern = true;
  int dx = array[1] - array[0];
  for (int i = 2; i < array.Length; i++)
    if ((array[i] - array[i - 1]) != dx)
    {
      constantPattern = false;
      break;
    }
  if (constantPattern)
    return array[array.Length - 1] + dx;

  // We have a complicated pattern.
  // Try and find a cyclical repeat of pattern elements.
  // At this time we always insist the pattern must start at the beginning of the array.
  int patternLength = FindCycle(array);
  if (patternLength > 0)
  {
    int repeats = array.Length / patternLength;
    int offset = array.Length - (repeats * patternLength);
    return array[offset];
  }

  // If there is no cyclical repeat of pattern elements,
  // there may still be a cyclical repeat of element increments.
  int[] increments = ToDifferenceArray(array);
  patternLength = FindCycle(increments);
  if (patternLength > 0)
  {
    int repeats = increments.Length / patternLength;
    int offset = increments.Length - (repeats * patternLength);
    return array[array.Length - 1] + increments[offset];
  }

  // Being unable to find a pattern, let's just return the last element.
  return array[array.Length - 1];
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.