Gosper 的序列 循环检测

 

// 高效程序的奥秘hacker's delight Henry S. Warren, Jr. 著冯速译

// 5 章位计数5.4 后缀0 计数

// 假设序列X0, X1, X2, ... 是由Xn+1 = f(Xn) 定义的。如果f 的值域是有限的,那么序列必定是循环的。

// 给定函数f , 循环检测问题就是找到第一个重复出现的元素的下标μ和周期λ.

// 参考:  http://home.pipeline.com/~hbaker1/hakmem/flows.html#item132  LOOP DETECTOR

// Knuth, Donald E. The Art of Computer Programming, Volume 2, Third Edition 3.1 节的问题6

 

 

#include "iostream"

#include "bitset"

#include "limits"

#include "time.h"

 

using namespace std;

 

#define out2(T) cout<<bitset<numeric_limits<unsigned int>::digits>(T)<<endl;

#define SIZE(T) (numeric_limits<unsigned T>::digits)

 

bool SHR31(int x)           //逻辑右移位

{

     return (unsigned int)x>>31;

}

 

#define HOUT(x,y,z)    hex_out(x,y,z)

 

inline void hex(int x)

{

     int w = cout.width();

     cout << "0x";

     cout.width(8); cout.fill('0');

     cout << hex << x << "  ";

     cout.width(w);

}

inline void hex(unsigned __int64 x)

{

     int w = cout.width();

     cout << "0x";

     cout.width(16); cout.fill('0');

     printf("%I64x   ",x);

     cout.width(w);

}

void hex_out(int x, int y, int z)

{

     hex(x); hex(y); hex(z); cout << endl;

}

 

int nlz(unsigned int x) {

     int n;

     if (x == 0) return 32;

     n = 1;

     if ((x >> 16) == 0) {n = n +16; x = x <<16;}

     if ((x >> 24) == 0) {n = n + 8; x = x << 8;}

     if ((x >> 28) == 0) {n = n + 4; x = x << 4;}

     if ((x >> 30) == 0) {n = n + 2; x = x << 2;}

     n = n - (x>>31);

     return n;

}

 

int ntz(unsigned int x)

{

     int n;

 

     if (x == 0) return 32;

     n = 1;

     if ((x & 0x0000FFFF) == 0) {n +=16; x >>= 16;}

     if ((x & 0x000000FF) == 0) {n += 8; x >>= 8;}

     if ((x & 0x0000000F) == 0) {n += 4; x >>= 4;}

     if ((x & 0x00000003) == 0) {n += 2; x >>= 2;}

     return n - (x & 1);

}

 

// 平方取中

int f(int a)

{

     return (a*a / 120) % 14400;

}

 

// 这里分五步论证算法的正确性:

// 0. f值域有限,则f 函数是循环,参考knuth 的证明

// 1. 设前面存的T[] 最大位置p, 现在查询最大位置kmax, 总有p = kmax

//    证:先证p 不小于且不大于kmax, p 存放的位置每次最大发生在2^i ,

//         : 10, 100, 1000, 10000 ...

//         设位长W = 8, 1000B 处是交界, ntz(1000B) = 3; kmax = 8-1 - nlz(1000B) = 7 - 4 = 3; i < 1000B, kmax <= p

//         i > 1111B , i = 10000B, p = 4, kmax = 8-1 - nlz(10000B) = 4, kmax = p, kmax p 更新

//         p 取所有位置的最大位置所以总有p = kmax, 不会造成访问越界,也不会造成访问遗漏.

// 2. T[] 数组有些元素总会被后来的元素覆盖, 但个别元素在一个周期内不会被覆盖.如果存在这种元素(称为异元素)

//     则总能在第二个周期λ中检测到循环

//     证:如奇数,由于ntz(奇数) = 0, 前一个奇数总被后一个奇数覆盖

//         若偶数,是不是在一个周期λ内有偶数不被覆盖,且不覆盖别的偶数呢?这一点很重要, 设这种偶数为异元素

//         因为被覆盖的元素在第二个周期都会反过来覆盖先前覆盖它的元素,

//         而到第二个周期λ检测中,这种异元素不会被反覆盖,当再一次走到这个值时,就可以检测到相同,

//         所以总能在第二个周期λ中检测到循环

// 3. 这种异元素总是存在的

//     证:举例λ= 10110101B, μ位置开始循环, 如果μ= 0, 10000000B 位置为异元素, 因为ntz(10000000B)

//         i < 11111111B 中最大的了, 如果 μ= 01001011B,  μ+ λ= 100000000B 这个元素独一无二的,

//         在下一个周期内μ+ λ+ λ< 1000000000B, 不可能有更大的p, p 先于kmax 更新,所以在一个周期内

//         这种独一无二的元素总是存在的

// 4. 对于周期λμ的求解也就迎刃而解了,可参考ITEM 132 和下面的程序

 

 

// 找出序列 的周期 λ (lambda) 和 开始位置 μ 的上下界 mu_l , mu_u

void ld_Gosper(int (*f)(int), int X0, int *mu_l, int *mu_u, int *lambda)

{

     int Xn, k, m, kmax, n, lgl;

     int T[33];

 

     T[0] = X0;

     Xn = X0;

     for (n = 1; ; n++) {

         Xn = f(Xn);

         kmax = 31 - nlz(n);                       // Floor(log2 n).

         for (k = 0; k <= kmax; k++) {

              if (Xn == T[k]) goto L;

         }

         T[ntz(n+1)] = Xn;                    // No match.

     }

L:

     // Compute m = max{i | i < n and ntz(i+1) = k}.

 

     m = ((((n >> k) - 1) | 1) << k) - 1;

     *lambda = n - m;

     lgl = 31 - nlz(*lambda - 1);              // Ceil(log2 lambda) - 1

     *mu_u = m;                                     // Upper bound on mu.

     *mu_l = m - __max(1, 1 << lgl) + 1;       // Lower bound on mu.

}

 

 

 

void test1();

void main()

{

     test1();

}

 

void test2()

{

     int (*p)(int) = f;

     int X0 = 32690;

     int *mu_l = new int; int *mu_u = new int; int *lambda = new int;

 

     ld_Gosper(p, X0, mu_l, mu_u, lambda);

 

     cout << (*mu_l) << " " << (*mu_u) << " " << (*lambda) << endl;

 

     delete mu_l; delete mu_u; delete lambda;

}

 

void test1()

{   

     int (*p)(int) = f;

     int X0 = 286;//123456;//2375;

     int *mu_l = new int; int *mu_u = new int; int *lambda = new int;

 

//   ld_Gosper(p, X0, mu_l, mu_u, lambda);

 

//   cout << (*mu_l) << " " << (*mu_u) << " " << (*lambda) << endl;

 

     int max = 0; int q = X0;

 

     for (int j = 1; ;j++) {

         X0 = j;

         ld_Gosper(p, X0, mu_l, mu_u, lambda);

         if (*lambda >= max) { max = *lambda; q = X0;}

         if (j >= 0x7FFF) break;

     }

 

     ld_Gosper(p, q, mu_l, mu_u, lambda);

     cout << (*mu_l) << " " << (*mu_u) << " " << (*lambda) << endl << endl;

 

     X0 = q;

     for (int i = 0; i < 50; i++) {

         cout << X0 << endl;

         X0 = p(X0);

     }

 

     delete mu_l; delete mu_u; delete lambda;

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值