找回密码
 欢迎注册
楼主: KeyTo9_Fans

[原创] 猜排列问题

[复制链接]
发表于 2019-10-30 17:29:16 | 显示全部楼层
忘记了我居然在这个帖子下面回复了这么多。

点评

有空的话 我再给博客 换一下更好的模板  发表于 2019-11-1 09:45
最近为了整理一些到wayne提供的blog,结果发现好多精彩的帖子,都忘光了  发表于 2019-10-31 10:40
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2019-11-1 08:48:40 | 显示全部楼层
试着测试一下随机mathe
代码假设Fans给出的目标数据就是按0,1,2,...,N-1顺序给出(但是mathe不知道)
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <math.h>
  6. #include <vector>

  7. #ifndef N
  8. #define N 20
  9. #endif

  10. #define TC 8

  11. #define TARGET 1

  12. struct Data{
  13.     char v[N];
  14.     int c;
  15. };

  16. std::vector<Data> usedlist;
  17. std::vector<Data> leftlist;
  18. char leftc;
  19. char avail[N][N];
  20. double tvalue;
  21. double ve;
  22. void init()
  23. {
  24.     tvalue = log(2*M_PI*N)/2+N*(log(N)-1)-log(TARGET);
  25.     ve=exp(-1.0);
  26.     int i,j;
  27.     leftc=N;
  28.     for(i=0;i<N;i++){
  29.        for(j=0;j<N;j++)avail[i][j]=j;
  30.     }
  31. }

  32. void dumpleft()
  33. {
  34.     int i;
  35.     int j;
  36.     for(i=0;i<leftlist.size();i++){
  37.        printf("\n");
  38.        for(j=0;j<N;j++)printf("%d ",leftlist[i].v[j]);
  39.     }
  40.     printf("\n");
  41. }

  42. char used[N];
  43. char mc[N*N];
  44. char umc[N*N];
  45. Data tryv;
  46. int depth;
  47. void search()
  48. {
  49.     if(depth>=N){
  50.         leftlist.push_back(tryv);
  51.         return;
  52.     }
  53.     int i,j;
  54.     for(i=0;i<usedlist.size();i++){
  55.         if(mc[i]>usedlist[i].c||umc[i]>N-usedlist[i].c)return;
  56.     }
  57.     for(i=0;i<leftc;i++){
  58.         int k = avail[depth][i];
  59.         if(used[k]==0){//The value has not been used, try use it
  60.             used[k]=1; tryv.v[depth]=k;if(k==depth)tryv.c++;
  61.             for(j=0;j<usedlist.size();j++){
  62.                 if(usedlist[j].v[depth]==k){
  63.                     mc[j]++;
  64.                 }else{
  65.                     umc[j]++;
  66.                 }
  67.             }
  68.             depth++;
  69.             search();
  70.             depth--;
  71.             for(j=0;j<usedlist.size();j++){
  72.                 if(usedlist[j].v[depth]==k){
  73.                     mc[j]--;
  74.                 }else{
  75.                     umc[j]--;
  76.                 }
  77.             }
  78.             used[k]=0;if(k==depth)tryv.c--;
  79.         }
  80.     }
  81. }

  82. void collect_lefts()
  83. {
  84.     depth=0;
  85.     tryv.c=0;
  86.     memset(used,0,sizeof(used));
  87.     memset(mc,0,sizeof(mc));
  88.     memset(umc,0,sizeof(umc));
  89.     search();
  90. }

  91. int trygenrand(Data& d)
  92. {
  93.     char cused[N];
  94.     memset(cused,0,sizeof(cused));
  95.     int i,j,k;
  96.     d.c=0;
  97.     for(i=0;i<N;i++){
  98.         j=rand()%leftc;
  99.         int choice=-1;
  100.         for(k=j;k<leftc;k++){
  101.            if(cused[avail[i][k]]==0){choice=k;break;}
  102.         }
  103.         if(choice<0){
  104.            for(k=0;k<j;k++){
  105.               if(cused[avail[i][k]]==0){choice=k;break;}
  106.            }
  107.         }
  108.         if(choice<0)return -1;
  109.         cused[avail[i][choice]]=1;
  110.         d.v[i]=avail[i][choice];
  111.         if(d.v[i]==i)d.c++;
  112.     }
  113.     return 0;
  114. }

  115. void genrand(Data& d)
  116. {
  117.     while(trygenrand(d)<0);
  118.     int i,j;
  119.     usedlist.push_back(d);
  120.     if(d.c==0){
  121.        for(i=0;i<N;i++){
  122.            for(j=0;j<leftc;j++){
  123.                if(d.v[i]==avail[i][j])break;
  124.            }
  125.            if(j>=leftc){
  126.                fprintf(stderr,"Internal error\n");
  127.                exit(-1);
  128.            }
  129.            for(;j<leftc-1;j++){
  130.                avail[i][j]=avail[i][j+1];
  131.            }
  132.        }
  133.        leftc--;
  134.     }
  135.     double dd=ve;for(i=0;i<d.c;i++)dd/=(i+1);
  136.     tvalue+=log(dd);
  137. }


  138. int main()
  139. {
  140.     Data d;
  141.     srand(time(NULL));
  142.     init();
  143.     int t=0;
  144.     while(leftc>TC){
  145.         genrand(d);
  146.         t++;
  147.     }
  148.     printf("try %d times to reach small probability, leftc = %d\n", t, leftc);
  149.     collect_lefts();
  150.     printf("Total %d left\n", (int)leftlist.size());
  151.     dumpleft();
  152. }
复制代码

测试了含20个数字,而mathe总是瞎猜直到听到12次Fans说你全错了位置在统计余下的可能
每次统计可能会很快(不到一秒)也可能会花费数分钟
试验结果
第一次:
try 72 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
第二次:
try 57 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

第三次:
try 53 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

第四次:
try 48 times to reach small probability, leftc = 8
Total 5 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
5 1 12 0 4 19 6 8 17 9 10 3 15 13 14 2 16 7 18 11
5 1 17 0 4 19 6 8 3 9 10 12 15 13 14 2 16 7 18 11
5 1 17 0 14 4 6 8 3 9 11 12 15 13 10 2 16 7 18 19
9 17 12 8 14 0 6 15 4 5 11 3 19 1 10 2 16 7 18 13
于是我们需要补猜一次
5 1 17 * 4 *  *************
就可以完全区分了。

第五次:
try 43 times to reach small probability, leftc = 8
Total 2 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
0 1 2 3 4 5 6 7 8 13 10 11 12 9 14 15 16 17 18 19

第六次:
try 42 times to reach small probability, leftc = 8
Total 2 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
0 1 2 3 4 5 6 7 8 9 19 11 12 13 14 15 16 17 18 10

第七次:
try 56 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

第八次:
try 70 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

第九次:
try 61 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

第十次
try 51 times to reach small probability, leftc = 8
Total 1 left

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

波动还是挺大的。
另外看来随机测试的次数还可以减少,提前分析规律会有好处。只是需要计算力非常强大,mathe远远不够。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2019-11-1 16:15:36 | 显示全部楼层
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <math.h>
  6. #include <vector>

  7. #ifndef N
  8. #define N 12
  9. #endif

  10. #define MINTC 3
  11. #define TC 6

  12. #define TARGET 5

  13. struct Data{
  14.     char v[N];
  15.     int c;
  16.     bool operator==(const Data& d)const{
  17.         int i; for(i=0;i<N;i++)if(v[i]!=d.v[i])return false;
  18.         return true;
  19.     }
  20. };

  21. std::vector<Data> usedlist;
  22. std::vector<Data> leftlist;
  23. char leftc;
  24. char avail[N][N];
  25. double tvalue;
  26. double ve;
  27. void init()
  28. {
  29.     tvalue = log(2*M_PI*N)/2+N*(log(N)-1)-log(TARGET);
  30.     ve=exp(-1.0);
  31.     int i,j;
  32.     leftc=N;
  33.     for(i=0;i<N;i++){
  34.        for(j=0;j<N;j++)avail[i][j]=j;
  35.     }
  36. }

  37. void filter(const Data& d)
  38. {
  39.     int i,j;
  40.     std::vector<bool> valid;
  41.     valid.resize(leftlist.size());
  42.     for(i=0;i<valid.size();i++)valid[i]=true;
  43.     for(i=0;i<leftlist.size();i++){
  44.         int c=0;
  45.         for(j=0;j<N;j++)if(d.v[j]==leftlist[i].v[j])c++;
  46.         if(c!=d.c)valid[i]=false;
  47.     }
  48.     for(i=0,j=0;i<leftlist.size();i++){
  49.         if(valid[i]){
  50.            if(i>j)leftlist[j]=leftlist[i];
  51.            j++;
  52.         }
  53.     }
  54.     leftlist.resize(j);
  55. }

  56. void getMaxDist(Data& d)
  57. {
  58.     int i,j;
  59.     int c[N];
  60.     int used[N];d.c=0;
  61.     for(i=0;i<N;i++)used[i]=0;
  62.     for(i=0;i<N;i++){
  63.         memset(c,0,sizeof(c));
  64.         for(j=0;j<leftlist.size();j++){
  65.              c[leftlist[j].v[i]]++;
  66.         }
  67.         int short_dist = 2*N+1, select=-1;
  68.         for(j=0;j<N;j++){
  69.              int v=2*c[j]-leftlist.size();
  70.              if(v<0)v=-v;
  71.              if(v<short_dist&&used[j]==0){
  72.                  short_dist=v;select=j;
  73.              }
  74.         }
  75.         if(select>=0){
  76.             used[select]=1; d.v[i]=select;if(i==select)d.c++;
  77.         }else d.v[i]=-1;
  78.     }
  79.     for(i=0,j=0;i<N;i++){
  80.         if(d.v[i]<0){
  81.             for(;j<N;j++){
  82.                 if(used[j]==0){
  83.                    used[j]=1;d.v[i]=j;if(i==j)d.c++;break;
  84.                 }
  85.             }
  86.         }
  87.     }
  88.     do{
  89.        for(i=0;i<usedlist.size();i++){
  90.            if(d==usedlist[i]){
  91.                break;
  92.            }
  93.        }
  94.        if(i==usedlist.size())return;
  95.        //If it has been used, randomly swap any two items
  96.        int s=rand()%N; int t=rand()%(N-1); if(t>=s)t++;
  97.        int tmp=d.v[s];d.v[s]=d.v[t];d.v[t]=tmp;d.c=0;
  98.        for(i=0;i<N;i++)if(d.v[i]==i)d.c++;
  99.     }while(1);
  100. }

  101. void output(const Data& d)
  102. {
  103.     int i;
  104.     for(i=0;i<N;i++){
  105.       printf("%d ",d.v[i]);
  106.     }
  107.     printf("\n");
  108. }

  109. void dumpleft()
  110. {
  111.     int i;
  112.     int j;
  113.     for(i=0;i<leftlist.size();i++){
  114.        printf("\n");
  115.        for(j=0;j<N;j++)printf("%d ",leftlist[i].v[j]);
  116.     }
  117.     printf("\n");
  118. }

  119. char used[N];
  120. char mc[N*N];
  121. char umc[N*N];
  122. Data tryv;
  123. int depth;
  124. int countlimit;
  125. int search()
  126. {
  127.     if(depth>=N){
  128.         leftlist.push_back(tryv);
  129.         if(leftlist.size()>countlimit)
  130.             return -1;
  131.         else return 0;
  132.     }
  133.     int i,j;
  134.     for(i=0;i<usedlist.size();i++){
  135.         if(mc[i]>usedlist[i].c||umc[i]>N-usedlist[i].c)return 0;
  136.     }
  137.     for(i=0;i<leftc;i++){
  138.         int k = avail[depth][i];
  139.         if(used[k]==0){//The value has not been used, try use it
  140.             used[k]=1; tryv.v[depth]=k;if(k==depth)tryv.c++;
  141.             for(j=0;j<usedlist.size();j++){
  142.                 if(usedlist[j].v[depth]==k){
  143.                     mc[j]++;
  144.                 }else{
  145.                     umc[j]++;
  146.                 }
  147.             }
  148.             depth++;
  149.             if(search()<0)return -1;
  150.             depth--;
  151.             for(j=0;j<usedlist.size();j++){
  152.                 if(usedlist[j].v[depth]==k){
  153.                     mc[j]--;
  154.                 }else{
  155.                     umc[j]--;
  156.                 }
  157.             }
  158.             used[k]=0;if(k==depth)tryv.c--;
  159.         }
  160.     }
  161.     return 0;
  162. }

  163. int collect_lefts(int limit)
  164. {
  165.     depth=0;
  166.     tryv.c=0;
  167.     memset(used,0,sizeof(used));
  168.     memset(mc,0,sizeof(mc));
  169.     memset(umc,0,sizeof(umc));
  170.     leftlist.clear();
  171.     countlimit=limit;
  172.     return search();
  173. }

  174. int trygenrand(Data& d)
  175. {
  176.     char cused[N];
  177.     memset(cused,0,sizeof(cused));
  178.     int i,j,k;
  179.     d.c=0;
  180.     for(i=0;i<N;i++){
  181.         j=rand()%leftc;
  182.         int choice=-1;
  183.         for(k=j;k<leftc;k++){
  184.            if(cused[avail[i][k]]==0){choice=k;break;}
  185.         }
  186.         if(choice<0){
  187.            for(k=0;k<j;k++){
  188.               if(cused[avail[i][k]]==0){choice=k;break;}
  189.            }
  190.         }
  191.         if(choice<0)return -1;
  192.         cused[avail[i][choice]]=1;
  193.         d.v[i]=avail[i][choice];
  194.         if(d.v[i]==i)d.c++;
  195.     }
  196.     return 0;
  197. }

  198. void genrand(Data& d)
  199. {
  200.     while(trygenrand(d)<0);
  201.     int i,j;
  202.     usedlist.push_back(d);
  203.     if(d.c==0){
  204.        for(i=0;i<N;i++){
  205.            for(j=0;j<leftc;j++){
  206.                if(d.v[i]==avail[i][j])break;
  207.            }
  208.            if(j>=leftc){
  209.                fprintf(stderr,"Internal error\n");
  210.                exit(-1);
  211.            }
  212.            for(;j<leftc-1;j++){
  213.                avail[i][j]=avail[i][j+1];
  214.            }
  215.        }
  216.        leftc--;
  217.     }
  218.     double dd=ve;for(i=0;i<d.c;i++)dd/=(i+1);
  219.     tvalue+=log(dd);
  220. }


  221. int main()
  222. {
  223.     Data d;
  224.     srand(time(NULL));
  225.     init();
  226.     int t=0;
  227.     while(t<N*N){
  228.         genrand(d);
  229.         t++;
  230.         if(t>=MINTC){
  231.            if(collect_lefts(100)>=0)break;
  232.         }
  233.     }
  234.     printf("try %d times to reach small probability, leftc = %d\n", t, leftc);
  235.     if(t>=N*N)
  236.        collect_lefts(100);
  237.     printf("Total %d left\n", (int)leftlist.size());
  238.     dumpleft();
  239.     while(leftlist.size()>1){
  240.         getMaxDist(d);
  241.         printf("Use: ");output(d);
  242.         usedlist.push_back(d);
  243.         filter(d);t++;
  244.     }
  245.     printf("Total tried %d times\n",t);
  246. }
复制代码

试验了上面的代码,先随机,减少到一定程度,就用贪心法选择看上去还行的方法,实验结果基本上在2N次以内解决。但是N不能太大,不然程序运行时间会很长。其中参数TC建议选择接近N/2,越小代码跑得越快,但是前面随机使用次数会越多。而参数MINTC表示前面这么多次必然全随机,不会探测是否需要切换到贪心法
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-11-22 16:47 , Processed in 0.023735 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表