现在的位置: 首页 > 综合 > 正文

洗牌问题

2018年04月26日 ⁄ 综合 ⁄ 共 2928字 ⁄ 字号 评论关闭

1的位置,很容易发现1的位置是从1->2->4->8……如果超过数尾则从头偏移,总之只要经过若干次移动,1再次移动在1的位置,就能够保证洗牌洗回了原序列。

描述

设2n张牌分别标记为1,2,…,n,n+l,…,2n,初始时这2n张牌按其标号从小到大排列。

经一次洗牌后,原来的排列顺序变成n+l,l,n+2,2,··,,2n,n。
即前n张牌被放到偶数位置2,4,·,·,2n,而后n张牌被放到奇数位置1,3,…,2n-l。
可以证明对于任何一个自然数n,经过若干次洗牌后可恢复初始状态。
编程任务:对于给定的n的值(n<=24000),编程计算最少经过多少次洗牌可恢复到初始状态。

输入
由键盘输入n的值。
输入包含多组数据,每行一个整数N。
输出
程序运行结束时,将计算出的最少洗牌次数在屏幕上输出。
对于输入的N,输出最少需要的洗牌次数
样例输入
10
样例输出
6

洗牌问题:

定理1:当第一张牌(牌1)回到初始位置时,所有的牌都回到了初始位置。

证明:设有一次操作,某牌在操作前处于位置r(1<=r<=2*N),那么,操作后,如果r原来是前一半的,显然r'=r*2; 否则,r'=(r-n)*2-1,即r'='r*2-(N*2+1);
将两个式子综合,可以得到r'= (r*2)%(N*2+1);
根据同余定理之一 ((i%m)*j)%m=(i*j)%M,可以整理出公式:i次操作后,该牌的位置为:
(r*(2^i))%(N*2+1);其中^表示乘方。
现在,我们假设经过M次操作后,牌1回到了初始位置,即(1*(2^M))%(N*2+1)=1时,再次应用同余定理,可以证明对于任意的k(1&lt;=k<=2*N),有(k*(2^M))%(N*2+1)=k,翻译成自然语言就是:当第一张牌回到初始位置时,所有的牌都回到了初始位置。命题得证。

定理2:一定存在某次操作M,这次操作后,所有的牌都回到了初始位置。
证明:我们已经证明了牌1回到初始位置时,所有的牌都回到初始位置,所以我们只需要证明牌1可以回到初始位置,即(2^M)%(N*2+1)=1一定有正整数解。证明这个定理前我们首先证明这样一个定理:
定理2.1:(2*r)%(2*n+1)==t
当t确定为1到2*n中的某值时(1<t<=2*n),r在1到2*n间有唯一解
因为2*n+1是奇数,我们只要看一下就会发现r到t是一个一一映射,当t为偶数时,显然r=t/2,当t为奇数时,r=(t+1)/2+n,

现在我们来证明定理2。运用反正法,若牌1永远不能回到初始位置,根据鸽笼定理,牌1必然陷入了某个不包含位置1的循环,因为下一状态仅和当前状态有关,当前状态最多只有2*N种,所以显然一定在不超过2*N+1次操作内出现重复状态。而重复状态则意味着循环。
因为我们假设这一循环不包括位置1,我们设f(i)为循环中某状态,f(0)=1,f(n+1)=(f(n)*2)%(2*N+1),f(j)为若干次循环后出现的重复状态,即f(i)=f(j)。因为循环一直持续,不妨假设j>i+i。又因为循环不包括状态1,即对于任意的k&gt;=i,f(k)!=1
根据定理2.1,我们可以根据当前状态推出上一状态,换句话说,若f(i)=f(j),则f(i-1)=f(j-1)。继续套用定理2.1,可以得到f(i-i)=f(j-i),即:f(j-i)=f(0)=1。又有假设j>i+i,即j-i>i,与另一假设对于任意的k>=i,f(k)!=1矛盾。
因此,可以证明,牌1所陷入的循环,必然是包含位置1的,即一定有某次操作,操作后牌1回到了初始状态。而且显然的,初始状态就是这循环中的一个状态。
因此命题得证。

例如当n==4时,过程如下:

5 1 6 2 7 3 8 4
7 5 3 1 8 6 4 2
8 7 6 5 4 3 2 1
4 8 3 7 2 6 1 5
2 4 6 8 1 3 5 7
1 2 3 4 5 6 7 8
经过6次

超时程序,用于看结果:

/*
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
	Scanner scanner=new Scanner(System.in);
	while(scanner.hasNext())
	{
		int number=scanner.nextInt();
		int sum=1,i;
		for(i=1;i<=2*number+1;i++)
		{
			sum*=2;
			if(sum>2*number+1)
				sum%=(2*number+1);
			if(sum==1)break;
		}
		System.out.println(i);
	}
	}
}*/
  

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
	Scanner scanner=new Scanner(System.in);
	while(scanner.hasNext())
	{
		int number=scanner.nextInt();
		int result[]=new int[2*number+1];
		for(int i=1;i<=2*number;i++)
		{
			result[i]=i;
		}
		int[] result1=new int[2*number+1];
		int[] finalresult=new int[2*number+1];
		for(int i=1;i<=2*number;i++)
		{
			finalresult[i]=result[i];
		}
		int sum=0;
		while(true)
		{
			sum++;
			int t=1;
			for(int i=2;i<=2*number;i+=2)
			{
				result1[i]=result[t++];
			}
			t=1;
			for(int i=1;i<=2*number;i+=2)
			{
				result1[i]=result[number+t++];
			}
			/*for(int i=1;i<=2*number;i++)
			{
				System.out.print(result1[i]+" ");
			}
			System.out.println();*/
			int flag=1;
			for(int i=1;i<=2*number;i++)
			{
				if(result1[i]!=finalresult[i])
				{
					flag=0;break;
				}
			}
			for(int i=1;i<=2*number;i++)
			{
				result[i]=result1[i];
			}
			if(flag==1)break;
		}
		System.out.println(sum);
	}
	}

}
               

ac代码:

 

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
	Scanner scanner=new Scanner(System.in);
	while(scanner.hasNext())
	{
		int number=scanner.nextInt();
		int sum=1,i;
		for(i=1;i<=2*number+1;i++)
		{
			sum*=2;
			if(sum>2*number+1)
				sum%=2*number+1;
			if(sum==1)break;
		}
		System.out.println(i);
	}
	}
}
        

抱歉!评论已关闭.