zoukankan      html  css  js  c++  java
  • poj 1597 Uniform Generator【生成指定范围内所有随机数】

    本文参考资料:http://hi.baidu.com/bnjyjncwbdbjnzr/item/1f997cfdd225d5d143c36a58

    题意:一个生成随机数的函数,

         Seed[x+1] =  seed[x] + STEP  % MOD

    输入step和mod,问能否生成0~MOD-1之间所有的数,是Good Choice,否则Bad Choice

    题意其实就是:给出SM,求0*S%M1*S%M2*S%M......(M-1)*S%M能否组成一个集合包含0.1.。。。M-1;(这个是原题意改造而来);

     

    算法:判断两个数是否互质;or 暴力解决

    其实暴力完全可以解决这个问题(b),只是其中用数学方法更加高效,巧妙;

    下面证明“如果SM互质则满足题意”:

    G=gcd(S,M);S=A*GM=B*G

    另设X=K*S%M=K*S-T*MT为整数,满足X属于0M-1其实就是说TK*S除以M的商。);

    X=K*A*G-T*B*G;因此取余后的整数一定是G的倍数。

    所以,上述要求证明的命题其实就是要证明:G只能取1才能满足条件;

    (个人感觉上述论证就是废话呵呵——SM互质不就是gcdSM==1吗?)

    (下面继续刚才的证明)

    充分性的证明:(即SM互质,则0M-1S倍对M取余一定能遍历0M-1

    只需证明的是,该余数中两两之间互不相等;

    假设k*Sb*SM取余相等(kb[0,M),并且kb不等);

    k*S=q1*M+rq2*M+r=b*S     <==>      k-b)*S=M*(q1-q2);

    因为题设SM互质,再由上式子可得M|k-b),这与kb[0,M),并且kb不等矛盾;

     

    因此题目命题得证。

    另外,偶然看到一个很牛叉的辗转相除法;

    int gcd(int a,int b)

    {

    while(b) b^=a^=b^=a%=b;

    return a;

    }

    此代码,很好很强大;把涉及位运算的交换的程序加入,便到得这段简洁高效的代码;

     

    注:AB;经过A^=B^=A^=B,结果就得到AB的交换;

     1 #include<stdio.h>
     2 long gcd(long a,long b);
     3 int main()
     4 {
     5     long step,mod;
     6     while(scanf("%ld%ld",&step,&mod)!=EOF)
     7     {
     8         if(gcd(step,mod)==1) printf("%10d%10d    Good Choice
    
    ",step,mod);
     9         else printf("%10d%10d    Bad Choice
    
    ",step,mod);
    10     }
    11     return 0;
    12 }
    13 long gcd(long a,long b)
    14 {
    15     long c;
    16     if(b==0) return a;
    17     c=a%b;
    18     while(c)
    19     {
    20         a=b;
    21         b=c;
    22         c=a%b;
    23     }
    24     return b;
    25 }

    下面是其他网友对该问题的论述:(感觉没有上面的讲的清楚,可以直接忽略)

    以数论的观点解释 poj1597 && 更相减损术

    http://blog.163.com/jiazheng2222@126/blog/static/1696323832009513104450808/

    可以想象成有一个人从起点0处绕着一个圈长为mod个单位跳步,每跳一次必须跳step个单位(也就是可不能出现在i*step<pos<(i+1)*step的位置),而至少跳mod次恰好回到起点的话,就是Good Choice,如果在第mod次前就恰好到达起点则为Bad Choice

    而我们知道,回到起点把所需的最短时间即是stepmod的最小公倍数,而若为Good Choice,显然最短时间又等于step*mod,由数论的知识gcd(step,mod)=step*mod/(stepmod的最小公倍数)=1

    bool check(long step,long mod)
    {
        long m=step>mod?step:mod;
        long n=step>mod?mod:step;
        long q=1;

        while(q!=0)
        {
            q=m-n;
            m=n>q?n:q;
            n=n>q?q:n;

        }
        if(m==1)
            return true;
        else
            return false;
    }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    http://hi.baidu.com/sjzezoi/item/6a3a71ccab9fee32b67a24fa

    题意:一个生成随机数的函数,

         Seed[x+1] =  seed[x] + STEP  % MOD

    问能不能生成0~MOD-1之间所有的数,是Good Choice,否则Bad Choice

     

    此题可以暴力模拟,但正解应该是gcd(STEP,MOD)==1

    G=gcd(a,b),于是有a=A*G,b=B*G(1<=A,B,gcd(A,B)=1) 对于a的多次加可以看成K*a(1<=k),转化成(K*a)%b的所有结果能否表示成0..b-1中的所有数,(K*a)%b=M,M=K*a-W*b(W为使M>0的最大整数),M=K*A*G-W*B*G M%G==0,既结果是G的倍数,如果想取得0..b-1中的所有数,那么必须G=1才可能..

    (代码仅供参考和学习,请不要直接粘贴刷AC数,期待你写出更好的代码)贡献一次PE

    暴力:

    #include<cstdio>

    #include<cstring>

    int a,b;

    bool vis[100010];

    int main()

    {

    while(scanf("%d%d",&a,&b)+1)

    {

    memset(vis,0,sizeof vis);

    int cnt=0,now=0;

    while(cnt<b)

    {

    if(vis[now]) break;

    vis[now]=1;

    now=(now+a)%b;

    cnt++;

    }

    if(cnt==b) printf("%10d%10d    Good Choice ",a,b);

    else printf("%10d%10d    Bad Choice ",a,b);

    }

    return 0;

    }

     

    正解:

    #include<cstdio>

    int a,b;

    inline int gcd(int x,int y)

    {

    if(!y) return x;

    return gcd(y,x%y);

    }

    int main()

    {

    while(scanf("%d%d",&a,&b)==1)

    {

    if(gcd(a,b)==1) printf("%10d%10d    Good Choice ",a,b);

    else printf("%10d%10d    Bad Choice ",a,b);

    }

    return 0;

    }

     

    http://hi.baidu.com/zhuangxie1013/item/cc5a130b05e77ad573e676b0

     这题其实是道数论题,但我把它当作模拟题来做了,虽然做出来了,但所用的空间和时间也大的吓人,此题输出时需要注意:输出格式为printf("%10d%10d    Bad Choice ",step,mod);即第二个数和Bad Choice之间有4个空格,下面是我模拟出来的程序:

    #include<iostream>
    #include<set>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;


    int main()
    {
    int step,mod,i,temp;
    set<int>s;
    while(scanf("%d %d",&step,&mod)!=EOF)
    {
       temp=0;
       s.insert(temp);
       for(i=1;i<mod;i++)
       {
        temp=(temp+step)%mod;
        s.insert(temp);
        if(s.size()!=(i+1))                                                                 //
    出现重复元素
        {
         printf("%10d%10d    Bad Choice ",step,mod);
         break;
        }

       }
       temp=(temp+step)%mod;
       if(temp==0&&s.size()==mod)
        printf("%10d%10d    Good Choice ",step,mod);
         s.clear();
    }
    system("pause");
    return 0;
    }

    看了别人的方法,讲的都是数论,所以将其转载:

    题目的意思就是一个生成随机数的函数,

         Seed[x+1] = seed[x] + STEP % MOD

    其中seed就是我们生成出来的随机数,至于seed[0]是哪个数并不重要,后面会证明。STEP就是我们每次往前一个所加的值,再moduleMOD得到下一个随机数。

    判断这个随机生成函数的好坏的依据是如果能够产生0~MOD-1内的所有数,就是一个好的,否则坏。

    我们知道了同余的特性,便可以假设在k步之后,生成的seed[k] = seed[0],所以由此有

            Seed[k] = seed[0] + STEP*k % MOD

    那么,

            STEP * k = MOD

    而我们如果要生产MOD个数,必须使k≥MOD,而且k不可能大于MOD,因为这个条件下生成的数又开始重复,所以k=MOD;在前面的条件下,如果STEPMOD有大于1的公约数例如d,那么会有STEP*(k/d) = MOD,这就是一个循环了,只会产生k/d<MOD个随机数。

    结论:iff gcd(STEP, MOD) == 1, good choice

     

  • 相关阅读:
    free
    wc
    awk
    wall
    sed
    Genymotion常见问题整合与解决方案
    Genymotion常见问题整合与解决方案
    java.net.MalformedURLException 异常
    java.net.MalformedURLException 异常
    异常:android.os.NetworkOnMainThreadException
  • 原文地址:https://www.cnblogs.com/huashanqingzhu/p/3523738.html
Copyright © 2011-2022 走看看