庞果答题:亿阳信通:不可表示的数 的一个人见解(8-13第二次更新。)

原题:

给定表达式[x/2] + y + x * y, 其中x,y都是正整数。其中的中括号表示下取整,例如[3/2] = 1 , [5/2]  = 2。

有些正整数可以用上述表达式表达出来,

例如正整数2,当取x = y = 1时,可以把2表达出来 ( 解释下:当x=y=1时, [x / 2] + y + x * y = [1 / 2] + 1 + 1 * 1 = 0+1+1 = 2 );

 有些数可以有多种方式表达,例如13可以由 x = 2 y = 4 以及x = 3 y = 3来表示; 

有些数无法用这个表达式表达出来,比如3。

 从1开始第n个不能用这个表达式表示出来的数,我们叫做an,例如a1=1 a2=3,给定n,求an。 

输入:n值 1<=n<=40 输出:an % 1000000007的结果(因为结果较大,输出an %1000000007的结果)。



个人分析:

表达式相当于 (x+1)*y+[x/2]

x=1...n 时,表达式分别为:

2*y  3*y+1  4*y+1  5*y+2 6*y+2  7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 ...

这时我们分析:

第一个表达式为 偶数

第二个表达式为 an-1 整除4=0;

第三个表达式为 an-1 整除5=0;

。。。

由此类推

我们在判断 an 这个数是否为 不可表达的数时,应用如下判断

an%2!=0

如果an>1那么 (an-1)%3!=0 并且 因为x=4时,表达式相同 我们也应判断 (an-1)%4!=0 

如果an>2那么 (an-2)%5!=0 并且 因为x=4时,表达式相同 我们也应判断 (an-2)%6!=0 

.....

那么合适结束呢?判断到an/2,x固定时第一个表达式为最小值


那么我们的判断函数应为:

 public static int givean(int n)
        {
            if (n < 1 || n > 40)
                return -1;
            double result = 0;
            int countN = 0;
           
            while (countN != n)
            {
                result += 1;
                 //* 以上相当于 (x+1)*y+[x/2]
                // * 表达式为:x=1...n :  2*y  3*y+1  4*y+1  5*y+2 6*y+2  7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 13+6 14+6 15+7 16+7 17+8 18+8 
                // * 在这里我们可以看出规律,

                // 当an=3时  我们只需判断 
                // 1 an-2是否能被5,6整除 
                // 2 an-1 是否能被3,4整除
                // 3 an 是否能被2整除
                //.....如果符合上述条件则可以表达,如果不符合则不可表达
                bool issuit = false;

                for (double i = 0; i < result/2; i++)
                {
                    double x = Math.Round(i*2, 0)+1;
                    if (Math.Abs(x - 1) > 0.00001d)
                    {
                        //eg: i=1 x=3 x1=4; i=2 x=5 x1=6;i=3 x=7 x1=8
                        double x1 = x + 1;
                        if (Math.Abs((result - i) % x - 0f) < 0.00001d || Math.Abs((result - i) % x1 - 0f) < 0.00001d)
                        {
                            issuit = true;
                            break;
                        }
                    }
                    else
                    {
                        if (Math.Abs(result % 2 - 0) < 0.00001d) //x=1 表达式为2*y 偶数全部可以表达
                        {
                            issuit = true;
                            break;
                        }
                    }
                }
                //
                if(!issuit)
                countN++;
            }

            return (int) (result % 1000000007);
        }

得到的结果为: a1=1 ,a2=3,a3=15,a4=63,a5=4095,a6=65535,a7=262143,a8=...

计算a8就要很长时间了。


这个解决方案是否正确尚未可知,我提交上去代码后却提示:




另外再提交C#代码时,注意将类名改为其他,他与主函数重名会报错。。。

另外不知道庞果检测代码是如何进行的,判断正确与否是怎么个机制,应该是提取方法验证结果与时间吧

挺有意思的,希望懂行的多交流交流。

看了一下,已经有16个大牛做出来了。


不过,这么多的问题没有答案什么的嘛,或者交流的地方。。。。

2013年8月13日6:38:20早晨 更新 

晚上睡觉的时候想了想,只能优化算法了,目标 取a40  3s内。。。

再循环的时候可以减轻一些压力,首先【 前取整】 代表有连续的两个数相等,所以我们循环的步长可以是2

修改代码如下:

public static int givean(int n)
        {
            Stopwatch stp=new Stopwatch();
            stp.Start();
            if (n < 1 || n > 40)
                return -1;
            if (n == 1)
                return 1;
            double result = 1;
            int countN = 1;
           
            while (countN != n)
            {
                //x=1 表达式为2*y 偶数全部可以表达 所以我们支取基数
                result += 2;
                 //* 以上相当于 (x+1)*y+[x/2]
                // * 表达式为:x=1...n :  2*y  3*y+1  4*y+1  5*y+2 6*y+2  7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 13+6 14+6 15+7 16+7 17+8 18+8 
                // * 在这里我们可以看出规律,

                // 当an=3时  我们只需判断 
                // 1 an-2是否能被5,6整除 
                // 2 an-1 是否能被3,4整除
                // 3 an 是否能被2整除
                //.....如果符合上述条件则可以表达,如果不符合则不可表达
                bool issuit = false;
                //y=1,2....n : 1+x+[x/2] 2+2x+[x/2] 3+3x+[x/2] ...n+nx+a
                //x的值取y=1时 的最大值为
                double limit = result - 1;
                //x=2,3...时的表达式判断[2/2]=[3/2] [x/2]=[x+1/2]所以x的步长为2,一次判断两个
                for (double x = 2; x <= limit; x += 2)
                {
                    //
                    //[x/2]
                    double yushu = Convert.ToInt64(x/2);
                    double a = x + 1;//系数
                    //
                    //limit = result - yushu - 1;
                    //
                    if (Math.Abs((result - yushu) % a - 0f) < 0.00001d ||
                        Math.Abs((result - yushu) % (a+1) - 0f) < 0.00001d)
                    {
                        issuit = true;
                        break;
                    }
                    
                }

                if (!issuit)
                {
                    countN++;
                    Console.WriteLine("N=" + countN + "  an=" + result);
                }
            }
            stp.Stop();
            TimeSpan ts = stp.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
            Console.WriteLine("Time taken : {0}", elapsedTime);
            return (int) (result % 1000000007);
        }

运行的结果是:

N=2  an=3
N=3  an=15
N=4  an=63
N=5  an=4095
N=6  an=65535
N=7  an=262143
Time taken?:?00:00:00.11


计算n=8时 需要5分多钟。。。。太让人郁闷了。

开来还是有优化的地方。。。。


2013年8月13日8:17:35 第二次更新

重新更改了一下:

这下取n=8 控制到5分钟以内了,代码如下:

 public static int givean(int n)
        {
            Stopwatch stp=new Stopwatch();
            stp.Start();
            if (n < 1 || n > 40)
                return -1;
            if (n == 1)
                return 1;
            double result = 1;
            int countN = 1;
           
            while (countN != n)
            {
                //x=1 表达式为2*y 偶数全部可以表达 所以我们支取基数
                result += 2;
                 //* 以上相当于 (x+1)*y+[x/2]
                // * 表达式为:x=1...n :  2*y  3*y+1  4*y+1  5*y+2 6*y+2  7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 13+6 14+6 15+7 16+7 17+8 18+8 
                //  2y+0   3y+1   4y+1 5y+2 6y+2 7y+3 8y+3 9y+4 10y+4
                //11y+5  12y+5   13y+6  14y+6 15y+7 16y+7 18y+8 19y+8 20y+9 21y+9
                //
                //eg:result=5 5*2/3=3 (3+1)*1+1=5
                long tmpx = Convert.ToInt64(result*2/3);
                if ((tmpx + 1 + Convert.ToInt64(tmpx / 2)) == Convert.ToInt64(result))
                    continue;
                    
                //
                // * 在这里我们可以看出规律,

                // 当an=3时  我们只需判断 
                // 1 an-2是否能被5,6整除 
                // 2 an-1 是否能被3,4整除
               
                //.....如果符合上述条件则可以表达,如果不符合则不可表达
                bool issuit = false;
                //y=1,2....n : 1+x+[x/2] 2+2x+[x/2] 3+3x+[x/2] ...n+nx+a
                //x的值取y=1时 的最大值为
                double limit = result - 1;
                //x=2,3...时的表达式判断[2/2]=[3/2] [x/2]=[x+1/2]所以x的步长为2,一次判断两个
                for (double x = 2; x <= tmpx; x += 2)
                {
                    //
                    //此x值得表达式为 a * y + yushu
                    double yushu = Convert.ToInt64(x/2);
                    double a = x + 1; //系数
                    double a2 = x + 2;
                    //
                    //此时,如果系数与加数同为偶数时 可以不用判断
                    //
                    if (Math.Abs(yushu % 2 - 0) > 0.00001d || Math.Abs(a % 2 - 0) > 0.00001d)
                    {
                        if (Math.Abs((result - yushu)%a - 0f) < 0.00001d)
                        {
                            issuit = true;
                            //Console.WriteLine(x+"");
                            break;
                        }
                    }// Math.Abs((result - yushu)%(a + 1) - 0f) < 0.00001d
                    if (Math.Abs(yushu % 2 - 0) > 0.00001d || Math.Abs(a2 % 2 - 0) > 0.00001d)
                    {
                        if (Math.Abs((result - yushu) % a2 - 0f) < 0.00001d)
                        {
                            issuit = true;
                            //Console.WriteLine(x+1 + "");
                            break;
                        }
                    }
                }

                if (!issuit)
                {
                    countN++;
                    Console.WriteLine("N=" + countN + "  an=" + result);
                }
            }
            stp.Stop();
            TimeSpan ts = stp.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
            Console.WriteLine("Time taken : {0}", elapsedTime);
            return (int) (result % 1000000007);
        }

想了一下,如果数据变大时比如10000001时,要验证的太多了,也即循环耗费时间太长了,

看来规律总结的不到位啊。


2013年8月14日15:05:09更新

看了http://blog.csdn.net/chengyxc/article/details/9958035 他这个算法似乎是对的,

符合的条件:n具有2^m-1的形式,并且,2n+1必定是个素数,这样的数才是不可能数

跟着更新了下算法。

但是 好像还是不能很好的实现。时间是个大问题,算到第21个数了,用时大约10s了。

肯定超限了。

算法如下:

 Stopwatch stp=new Stopwatch();
            stp.Start();
            long inf = 140000000;// int.MaxValue;// 264800000;// 2147483637; 
            //List<int> prime = new List<int>();
            int[] prime = new int[inf];
            
            prime[0] = prime[1] = 0;
            for (int i = 2; i < inf; ++i)
                prime[i] = 1;
            for (int i = 4; i < inf; i += 2)
                prime[i] = 0;
            double t = Math.Sqrt(inf);
            for (int i = 3; i <= t; i += 2)
            {
                if (prime[i]>0)
                {
                    int k = i * i, p = i + i;
                    for (int j = k; j < inf; j += p)
                    {
                        prime[j] = 0;
                        //if ((j - 1)%2 == 0)
                        //    Console.WriteLine("j="+j+"  "+(j - 1) /2 + "");
                    }
                }
            }
            //以上得到了 1 -- inf 范围 中的所有素数,在素数中判断条件
            int count = 1;
            for (long i = 0; i < prime.LongLength; i++)
            {
                if (prime[i] != 0)
                {
                    if ((i - 1)%2 == 0)
                    {
                        //符合2n+1为素数
                        double n = (i - 1)/2;
                        if (n < 3)
                            continue;
                        //符合2^M-1
                        double a = Math.Log(n + 1, 2); // Math.Log(2, n + 1);
                        double b = (a - Convert.ToInt64(a));
                        if (a != double.NaN && a > 1 && Math.Abs(a - Convert.ToInt64(a)) < 0.000001d)
                        {
                            count++;
                            //Console.WriteLine(string.Format("n={0} an={1}",count,n));
                        }
                    }
                }
            }
            stp.Stop();
            Console.WriteLine(stp.ElapsedMilliseconds/1000d+"S");
            //
输出结果为:

n=2 an=3
n=3 an=15
n=4 an=63
n=5 an=4095
n=6 an=65535
n=7 an=262143
n=8 an=2097150
n=9 an=8388606
n=10 an=33554409
n=11 an=33554418
n=12 an=33554429
n=13 an=33554439
n=14 an=67108824
n=15 an=67108844
n=16 an=67108878
n=17 an=67108886
n=18 an=67108889
n=19 an=67108890
n=20 an=67108899
n=21 an=67108901
9.854S

我的是32位系统,数组最大长度只能设为:264800000 再长就报超出内存了。也不知道是不是系统的问题。64位系统的验证一下吧。



评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值