皮萨诺时期的快速计算

时间:2016-07-29 12:22:24

标签: c++ fibonacci

我想在不到1秒内计算数字 m 的Pisano时段。 这是我目前在C ++中的代码:

#include <iostream>
#include <vector>

using std::vector;

bool is_equal(vector<long long> v, long k) {

  if (k == 0) return false;
  // compare first and second half of array
  for (long i = 0, j = k; i < k, j < v.size(); ++i, ++j) {
    if (v[i] != v[j]) return false;
  }

  return true;
}

long long get_pisano_period(long long m) {

  vector<long long> v;

  long long a = 0; long k = 0; long long b = 1;
  // loop until repetition is found
  while (!is_equal(v, k)) {
    v.push_back(a % m);
    long long tmp = a + b;
    a = b;
    b = tmp;
    k = v.size() / 2;  // the mid point
  }
  return k;
}

对于大 m ,这不会终止。我该怎么做才能加快计算速度?我在类型中犯了错误吗?

编辑:

我已将 tmp 的类型更改为 long long ,但仍然失败。

尝试不同的值后,程序将终止所有值,直至 m = 9 ,但 m = 10 的周期 60 。 我怀疑溢出是非终止背后的原因。有什么建议?

4 个答案:

答案 0 :(得分:2)

问题是值增长得非常快,您可能只想存储模数,因为它也可以工作:

修改后的代码:

#include <algorithm>
#include <vector>

size_t get_pisano_period(long m) {
    std::vector<long> v{1, 1};
    while (true) {
        auto t = (v[v.size() - 1] + v[v.size() - 2]) % m;
        v.push_back(t);
        if (t == 0 && v.size() % 2 == 0 &&
            std::equal(v.begin(), v.begin() + v.size() / 2,
                       v.begin() + v.size() / 2, v.end())) {
            return v.size() / 2;
        }
    }
    return v.size() / 2;
}

此代码与您之间唯一的算法区别是,我只存储了m的除法余数,而不是存储斐波那契序列的值。

此外,众所周知,pisano序列包含1,2或4个零,因此通过仅在t == 0时测试相等性,您应该大大减少相等测试的数量。在我的计算机上,对于较大的mt == 0的版本的速度是没有版本的两倍。

注意:如果您的编译器不支持C ++ 14,请删除调用std::equal中的最后一个参数。

“测试”代码:

int main () {
    for (auto i = 2LL; i < 100; ++i) {
        std::cout << get_pisano_period(i) << ' ';
    }
    std::cout << '\n';
}

输出:

  

1 8 6 20 24 16 12 24 60 10 24 28 48 40 24 36 24 18 60 16 30 48 24 100 84 72 48 14 120 30 48 40 36 80 24 76 18 56 60 40 48 88 30 120 48 32 24 112 300 72 84 108 72 20 48 72 42 58 120 60 30 48 96 140 120 136 36 48 240 70 24 148 228 200 18 80 168 78 120 216 120 168 48 180 264 56 60 44 120 112 48 120 96 180 48 196 336 120

答案 1 :(得分:1)

a在当前程序中溢出。如果你计算它,模数计算的最终结果将是相同的。

因此,通过将a = b更改为a = b%m,我们可以避免溢出:

#include <iostream>
#include <vector>

using std::vector;

bool is_equal(const vector<long long>& v, long k) {
    auto size{v.size()};
    if(k == 0) return false;
    // compare first and second half of array
    for(long i = 0, j = k; i < k && j < v.size(); ++i, ++j) {
        if(v[i] != v[j]) return false;
    }

    return true;
}

long long get_pisano_period(long long m) {

    vector<long long> v;

    long long a = 0; long k = 0; long long b = 1;
    // loop until repetition is found
    while(!is_equal(v, k)) {
        v.push_back(a % m);
        long long tmp = a + b;
        //a = b; // this grows too large
        a = b%m; // better
        b = tmp;
        k = v.size() / 2;  // the mid point
    }
    return k;
}

int main()
{
    auto testval{get_pisano_period(10)};
    std::cout << testval << '\n';
}

答案 2 :(得分:1)

以下是同一迭代算法的两种实现,该算法根据以下约束为任何给定的模 n 生成Pisano周期序列:

1 <= n <= CR

CR代表您的计算资源。就我而言,CR大约为10,000,000,000。

该算法是基于以下思想构造的:Pisano序列始终以0和1开头,并且可以通过为每个数字构造前面的余数,并为每个数字构造取模 n 的斐波那契数列。考虑到模 n

C#

public static IEnumerable<int> PisanoPeriodicSequence(int n)
{
    int current = 0, next = 1;

    yield return current;

    if (n < 2) yield break;
    next = current + (current = next);

    while (current != 0 || next != 1)
    {
        yield return current;
        next = current + next >= n ? current - n + (current = next) : current + (current = next);
    }
}

C ++

std::vector<int> pisano_periodic_sequence(int n)
{
    std::vector<int> v;
    int current = 0, next = 1;

    v.push_back(current);

    if (n < 2) return v;
    current = (next += current) - current;

    while (current != 0 || next != 1)
    {
        v.push_back(current);
        current = current + next >= n ? (next += current - n) + (n - current) : (next += current) - current;
    }

    return v;
}

答案 3 :(得分:0)

在阅读代码之前,请注意:

1。期间始终以“ 01”开头。

2.pisano号是偶数。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
 int m; cin>>m;
 vector<int> v;
 cout << "sequence = ";
 v.push_back(0); cout<< v[v.size()-1]<< " ";
 v.push_back(1); cout<< v[v.size()-1]<< " ";

 while (true)
 {
    v.push_back((v[v.size()-2] + v[v.size()-1]) % m); // saving the remainder only
    cout<< v[v.size()-1]<< " ";
    // true if all elemenets between the begining and half way are the same as the elements between the halfway and the end 
    bool sequence = equal(v.begin(), v.begin() + v.size() / 2, v.begin() + v.size() / 2, v.end());
    bool even = v.size() % 2 == 0;
    if (even && sequence)
    {
        cout<< "\nPisano number = "<< v.size() / 2;
        break;
    }
  }
}

输出:

  1. 对于m = 3:

    3 sequence = 0 1 1 2 0 2 2 1 0 1 1 2 0 2 2 1 Pisano number = 8

  2. 对于m = 99:

    99 sequence = 0 1 1 2 3 5 8 13 21 34 55 89 45 35 80 16 96 13 10 23 33 56 89 46 36 82 19 2 21 23 44 67 12 79 91 71 63 35 98 34 33 67 1 68 69 38 8 46 54 1 55 56 12 68 80 49 30 79 10 89 0 89 89 79 69 49 19 68 87 56 44 1 45 46 91 38 30 68 98 67 66 34 1 35 36 71 8 79 87 67 55 23 78 2 80 82 63 46 10 56 66 23 89 13 3 16 19 35 54 89 44 34 78 13 91 5 96 2 98 1 0 1 1 2 3 5 8 13 21 34 55 89 45 35 80 16 96 13 10 23 33 56 89 46 36 82 19 2 21 23 44 67 12 79 91 71 63 35 98 34 33 67 1 68 69 38 8 46 54 1 55 56 12 68 80 49 30 79 10 89 0 89 89 79 69 49 19 68 87 56 44 1 45 46 91 38 30 68 98 67 66 34 1 35 36 71 8 79 87 67 55 23 78 2 80 82 63 46 10 56 66 23 89 13 3 16 19 35 54 89 44 34 78 13 91 5 96 2 98 1 Pisano number = 120 请注意,您可以删除这两个布尔变量以减少一些比较,但是我保留了它们作为说明。