继续整数数组的模式

时间:2015-09-19 11:51:34

标签: algorithm statistics pattern-matching prediction

给定一个或多个长度相等的整数数组的集合,我希望预测最有可能的下一个数组。元素通常只增加1或跳回零,但其他变化肯定是可能的。

示例1:

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

示例2:

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

示例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]

案例1和案例2很容易发现,但我很难弄清楚如何检测示例3中的模式。我需要哪些关键字来谷歌才能取得进展?< / p>

编辑:对保罗的回应。 尽管模式中的每个元素看起来都像任何东西,但如果模式不是以某种方式从不断添加和循环重置为零而构建,则模式已经非常无意义,以至于我的算法不再需要做得更好。所以我不关心复杂的多项式或[+ 1,+ 1,+ 2,-5,+ 7]加法规则。

2 个答案:

答案 0 :(得分:1)

所以,如果我做对了,在给定的输入中你有

  • 常数(如示例1,第1列)
  • 恒定添加(如示例1,第3列)
  • 循环(示例3第3列)

我想将两者中的任何一个或其中三个组合在一起是不错的(如例3中,第2列是常数和常数相加的组合)。

首先,我们必须考虑到,对于任何给定的输入,我们必须考虑到所有情况都可能发生在一列。您可以使用不同的变量为任何这些情况,结构,甚至没有任何变量创建对象。

其次,您必须检查每一列。因此,虽然未完全检查该列,但我们会查找多个内容:

  • 是否有一个常数? (两个连续的行具有相同的数字)。如果它是真的,我们在变量中记住常量和最后一行。

  • 两个连续数字之间有区别吗?如果是,那么我们有不断的加法,我们在变量中记住它在加法中使用的常数和加法发生的位置。注意!我们必须记住,添加可以在3个常数之后发生。因此,如果我们有一个常数和恒定的加法,我们必须看到常数停止的位置,以便我们可以在加法后继续它。

  • 有没有循环?第一个数字是否等于同一列中的任何其他数字,但是其他任何行?如果发生这种情况,我们只需记住循环开始后添加多少(AND / OR常数)。这是最棘手的一个:我们可以有常数,恒定加法和循环。但它也不难,因为以前我们有恒定数量和不断增加。所以,一个循环只是那两个重复的循环。

这是使用您提供给我们的信息查找简单模式的算法。我希望它有用。

答案 1 :(得分:0)

使用格鲁吉亚的基本想法,我放弃了试图在20行或更少的时间内完成这项工作。下面的代码肯定没有检测到所有模式,但它对我的目的来说已经足够了。

它将能够识别由重复数字或重复增量组成的模式: enter image description here

虽然它无法检测到二阶模式,例如: 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];
}