【问题标题】:How to optimize my Langford Sequence function?如何优化我的 Langford 序列函数?
【发布时间】:2015-09-25 01:39:38
【问题描述】:

这是我的代码,用于从一组数字对 (112233 -> 312132) 中生成 Langford 序列。我想写一个递归函数,因为我无法在网上任何地方找到一个递归函数作为使用算法的自我改进练习。我的问题是,如何优化它?有没有办法将动态编程应用于此并具有更好的时间/空间复杂度,并强调时间复杂度?我当前的运行时复杂度为 O(n^2),空间复杂度为 O(n)。在编写更简洁的代码方面的任何帮助也值得赞赏。谢谢。还有,这是一个 P 问题还是一个 NP 问题?

#include <iostream>
using namespace std;

const int arrLen = 8;
const int seqLen = 8;

bool langfordSequence(int * arr, int indx, int *seq, int pos);
int main() {

    int arr[] = {1,1,2,2,3,3,4,4};
    int seq[] = {0,0,0,0,0,0,0,0};

    bool test = langfordSequence(arr, 0, seq, 0);

    if (test)
        cout << "Langford Sequence Successful: " << endl;
    else
        cout << "Langford Sequence Failed: " << endl;

    for (int i = 0; i < seqLen; i++)
    {
        cout << seq[i] << " ";
    }   
    return 0;
}

bool langfordSequence(int * arr, int indx, int *seq, int pos)
{

    if (indx >= arrLen - 1) //this means we've reached the end of the array
        return true;

    if (pos + arr[indx] + 1 >= seqLen)  //if the second part of the number is off the array
        return false;

    if (seq[pos] == 0 && seq[pos + arr[indx] + 1] == 0)
    {
        seq[pos] = arr[indx];
        seq[pos + arr[indx] + 1] = arr[indx];
        if (langfordSequence(arr, indx + 2, seq, 0))    //the current pair is good, go to the next one, start from the beginning
            return true;
        else
        {
            seq[pos] = 0;
            seq[pos + arr[indx] + 1] = 0;
            if (langfordSequence(arr, indx, seq, pos + 1))
                return true;
        }
    }
    else
    {
        if (langfordSequence(arr, indx, seq, pos + 1))  //current position is no good, try next position
            return true;
    }
}

【问题讨论】:

  • 阅读该问题后首先想到的想法是:如果您可以枚举较低 n 的挂钩序列,则求解较高 n 变成了看你是否可以将两个元素插入到合适大小的间隙中的问题。挂钩序列类似于 121_2 或 2_121。两者都有一个间隙,您可以将 3 插入其中并获得 312132 或 231213。
  • 问题空间中还有一个对称性,即任何解决方案的反面都是一个解决方案。
  • 你确定你的算法在O(n^2)时间运行吗?如果我正确理解您的算法,最坏的情况是您尝试将每个输入放在每个可能的位置,直到找到解决方案。那就是O(2^n)运行时间...
  • @VincentvanderWeele 你是对的。它是 O(2^n),我仍然掌握测量时间复杂度的窍门。找出递归算法的时间复杂度感觉很棘手。呃,这个算法好慢!
  • @Lorehead 您的意思是不要从第一个位置重新开始,而是跟踪未平仓头寸,看看它们是否直接有效?我已经想到了这一点,但我是否还必须在阵列中搜索才能找到一个空位?也许我可以有另一个数组来存储打开的插槽。 IE。第一个元素存储第一个空槽,第二个元素存储第二个空槽,等等。但我仍然无法使其小于 O(2 ^n) 对吧?

标签: c++ algorithm recursion time-complexity dynamic-programming


【解决方案1】:

这是我在 cmets 中提到的想法的伪代码。我还没有搜索过还有谁做过这样的事情(因为我喜欢先自己解决问题),但其他人可能有优先权。

算法 LANGFORD 参数 N(顶层,最终序列中的最大元素),M(中间,钩子序列中的最大元素)。在顶层,M = N。 返回: 长度为 2N 的所有序列的列表,使得 1..M 中的每个元素 j 恰好被 j 个元素隔开两次,并且第二个 M 的位置小于 N + M/2 + 1。序列设置为0。

If M == 1 (base case)
  Let S' := []
  For i := 0 to N-2
    Let s' be the length 2N sequence containing the subsequence "101" starting at position i (counting from 0), and zero everywhere else.
    Insert s' into S'
  Return S'

Otherwise: (inductive case)
Let S' := []
Let S := LANGFORD(N,M-1)
For each s in S
  Let r := reverse(s)
  For i := 0 to floor(N - M/2 + 1)
    If s[i] == s[i+M+1] == 0
      Let s' be s with s'[i] and s'[i+M+1] replaced by M
      Insert s' into S'
    If r != s and r[i] == r[i+M+1] == 0
      Let r' be r with r'[i] and r'[i+M+1] replaced by M
      Insert r' into S'
Return S'

在 N = 4 时运行此算法,我们最初有 M = 4 并递归直到 N = 4,M = 1。这一步为我们提供了列表 [[10100000],[01010000],[00101000]]。我们将其传递回 M=2 步骤,该步骤找到挂钩序列 [[12102000]、[10120020]、[20020101]、[02002101]、[00201210]、[01210200]、[20021010]、[00201210]、 [20121000],[02012100]]。将这些传递到 M=3 步骤,我们得到 [[30023121],[13120320],[13102302],[31213200],[23021310],[23121300],[03121320]]。最后,我们回到顶层函数,找到序列[[41312432]],也表示它的对称对偶23421314。

基本上,我们正在尝试将每个拼图(例如“30003”)放入每个潜在的解决方案中,请记住,任何解决方案的镜像都是一个解决方案。时间和空间复杂度主要由 N/2 附近的 M 值的潜在解决方案的组合爆炸决定。将序列存储为对齐以使用向量指令的字节数组,并将列表存储为数组列表(C++ 中的向量,Haskell 中的 [sequence] 等)可能会很快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多