【问题标题】:Fast Computation of Pisano Period皮萨诺周期的快速计算
【发布时间】:2016-12-04 04:00:07
【问题描述】:

我想在不到 1 秒的时间内计算出一个数字 m 的皮萨诺周期。 这是我目前在 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,但对于周期为 60m = 10 则失败强>。 我怀疑溢出是不终止的原因。有什么建议?

【问题讨论】:

  • m 到底有多大?你是在调试模式还是发布模式下编译?
  • 看起来您正在按值传递向量。也许通过引用传递?
  • i &lt; k 在逗号之前可能没有任何区别。
  • 按照 flatmouse 的建议 bool is_equal(const vector&lt;long long&gt; &amp;v, long k) {i &lt; k &amp;&amp; j &lt; v.size(); 开始。
  • 您可能希望在值溢出之前在代码前面取值的模数。目前a 溢出,然后模值不再正确。

标签: c++ fibonacci


【解决方案1】:

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

修改后的代码:

#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 时测试相等性,您应该大大减少相等性测试的数量。在我的电脑上,对于较大的m,带有t == 0 的版本的速度是不带t == 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 240 40 48 38 24 76 18 56 240 40 48 38 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

【讨论】:

    【解决方案2】:

    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';
    }
    

    【讨论】:

      【解决方案3】:

      以下是相同迭代算法的两个实现,它在以下约束下为任何给定的模 n 生成 Pisano 周期序列:

      1

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

      该算法是围绕 Pisano 序列始终以 0 和 1 开始的思想构建的,并且可以通过添加前面的余数和考虑到模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;
      }
      

      【讨论】:

        【解决方案4】:

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

        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

        【讨论】:

          猜你喜欢
          • 2020-10-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-21
          • 2010-12-23
          • 2011-11-27
          相关资源
          最近更新 更多