如何进行有效定制输出的排列

时间:2014-07-30 17:06:06

标签: ruby arrays algorithm permutation

这是一个面试问题,我没有回答,但仍然对如何解决感到好奇。

你有大家庭的N人,分别为1,2,3,......,N岁。 你想拍一张你这个大家庭的照片。你们所有的家人都会排成一排。

“我,这个家庭的朋友,建议如下安排家人:”

  1. 1岁的家庭成员将坐在该行的左端。
  2. 每两个坐在一起的家庭成员的年龄差异不得超过2年。
  3. 输入:整数N,1≤N≤55。

    输出:摄影师可拍摄的照片数量。

    示例 - >输入:4,输出:4

    符合条件的数组:

    [1,2,3,4] [1,2,4,3] [1,3,2,4-] [1,3,4,2]

    另一个例子:

    输入:5输出:6

    符合条件的数组:

    [1,2,3,4,5] [1,2,3,5,4] [1,2,4,3,5] [1,2,4,5,3] [1, 3,2,4,5] [1,3,5,4,2]

    我在Ruby中选择了这个问题,通过生成每个排列并过滤它们来解决这个问题,首先检查条件#1,确保数组的第一个条目== 1,这很好,快。

    其次是从左到右走每个阵列并确保每对的绝对值差异不超过2.这是可怕的和缓慢的。

    我的实现,当N>时变得很慢9.因为它正在排序300k排列。 从那里开始的时间是二次的(我相信,仍然可以解决这个问题)。

    我应该如何改善这种情况?

    我不是真的在这里寻找代码示例,更多的想法,应该使用哪种算法来有效地排列排列?我应该写自己的排列(可能是的)。

    在这些方面,我确实复制了此版本的QuickPerm https://stackoverflow.com/a/2390969/1265528

    results << yield(a)附近添加一个条件,只选择以1开头的数组,但我不确定如何最好地实现上述其他条件。

    修改

    这是令人难以置信的令人失望的解决方案。

    我真的想弄清楚如何生成排列,而不是表示可能的正确排列数的整数。 -

    def number_of_possible_perms(n_persons)
      array = []
      array[0] = 0
      array[1] = array[2] = 1
      for i in 3..n_persons
        array[i] = array[i-1] + array[i-3] + 1
      end
      return array[n_persons]
    end
    

4 个答案:

答案 0 :(得分:8)

如果我们绘制出可能的转换,那么应该更清楚地说明如何解决这个问题:

  2   6---8
 /|\ /|\ /|\
1 | 4 | 7 | 10...etc
 \|/ \|/ \|/
  3---5   9

让每个数字只接触一次并从1开始的路径总数为C_n,其中n是节点数。让我们考虑一些可能的情况:

  • C_1 = 1
  • C_2 = 1
  • C_3 = 2

现在假设n&gt; 3.让我们考虑一些可能的序列:

  • 1,2,...我们知道如果它以这种方式开始,我们可以通过删除1并将2设置为开始来重新排列我们的图形,并且它与从1到n-1的图形相同。所以我们有从1,2开始的C_(n-1)序列。
  • 1,3,2,...我们可以在这里做同样的事情,因为我们的下一步必须是4.重新排列图表从4开始,我们有从1,3开始的C_(n-3)序列, 2。
  • 1,3,4,......我们在这里有两种可能:要么我们只有4个节点和1个序列(1,3,4,2),要么我们有4个以上的节点和0个序列。
  • 1,3,5,...我们又有两种可能性:我们只有4个节点和0个序列,或者我们有超过4个节点和1个序列(一旦你上升了2个(之后) 3)你必须上升到2,直到你到达终点,然后下降2)。

所以我们现在有C_n = C_(n-1)+ C_(n-3)+ 1.

答案 1 :(得分:0)

我不确定是否存在针对此问题的优化算法,但我想到的是一种蛮力方法backtracking

它比迭代所有可能的预先计算的排列更有效,因为如果找到第一个非匹配对,它可以立即停止。因此,它可以修剪搜索空间的大部分(换句话说,它不必查看所有N!排列)。

答案 2 :(得分:0)

创建一个方法f,它接受​​N并返回可能数组的数组。在这样做时,使用递归。当N = 1时,您知道可能性仅为[1],因此输出应为[[1]]。否则,请计算f(N - 1)。要从f(N)获取N,请考虑f(N - 1)中每个数组中可以插入哪些位置{{1}}。

答案 3 :(得分:0)

我会这样做。

<强>条款

  • N:人数
  • A(n,i):满足第一个n家庭成员之间排序要求(“可行排列”)的所有排列的集合,以使人年龄i为最后一个,{{ 1}}。

<强>目标

确定2 <= i <= n取消所有A(N,i)的联合。

<强>算法

i=2..N

只有一种方式可以命令1和2人:

n = 2

以及两种订购前3种方式:

A(2,2) = { [1,2] }

n = 3

当我们考虑前四个时,我们开始变得更有趣了。最初,无视相邻成员年龄差异最多两年的要求。

A(3,2) = { [1,3,2] }
A(3,3) = { [1,2,3] }

n = 4

注意这些集合是如何确定的。考虑A(4,2) = { [1,4,3,2], [1,3,4,2] } A(4,3) = { [1,4,2,3], [1,2,4,3] } A(4,4) = { [1,3,2,4], [1,2,3,4] } 。我们在A(4,2)中采用单个排列,并将A(3,2)插入两个可能的位置。

我们接下来删除所有不满足相邻年龄差异要求的组合,并留下以下内容:

4

同样,首先计算A(4,2) = { [1,3,4,2] } A(4,3) = { [1,2,4,3] } A(4,4) = { [1,3,2,4], [1,2,3,4] } n=5 的集合,而不参考相邻的年龄要求:

n=5

请注意这些集合是如何从A(5,2) = { [1,5,3,4,2], [1,3,5,4,2], [1,3,4,5,2] } A(5,3) = { [1,5,2,4,3], [1,2,5,4,3], [1,2,4,5,3] } A(5,4) = { [1,5,3,2,4], [1,3,5,2,4], [1,3,2,5,4], [1,5,2,3,4], [1,2,5,3,4], [1,2,3,5,4] } A(5,5) = { [1,3,4,2,5], [1,2,4,3,5], [1,3,2,4,5], [1,2,3,4,5 } A(4,2)A(4,3)A(4,4)组合生成的。要计算A(5,2),例如,对于A(5,2)中的每个枚举(只有一个),我们会在第一个和最后一个之后的每个位置插入A(4,2)

现在删除所有可行的permations,留下我们:

5

以这种方式继续,直到为A(5,2) = { [1,3,5,4,2], [1,3,4,5,2] } A(5,3) = { } A(5,4) = { [1,3,5,2,4], [1,2,3,5,4] } A(5,5) = { [1,2,4,3,5], [1,3,2,4,5], [1,2,3,4,5 } 计算A(N,i)

如果i=2,...N,可行的排列将是这四组的结合:

N => 5

我希望通过一次消除一组排列来应用额外的逻辑来加速计算。