E81086713E446D36F62B2AA2A3502B5EB155

Java杂家

杂七杂八。。。一家之言

BlogJava 首页 新随笔 联系 聚合 管理
  141 Posts :: 1 Stories :: 174 Comments :: 0 Trackbacks

2009年4月10日 #

如题:求连续正整数使得其和为给定的一个正整数
下面给出我的解法,几乎可以一步到位求出来
实现代码如下:
/**
*Author: Koth (
http://weibo.com/yovn)
*Date:  2011-12-01
*/
#include 
<stdlib.h>
#include 
<stdio.h>
#include 
<stdint.h>

int solve(int Y,int& X){
    
int m=0;
    
int t=Y;
    
if(Y<=0){
        X
=Y;
        
return 1;
    }
    
while((t&1)==0){
        m
+=1;
        t
=t>>1;
    }
    
if(m==32){
        X
=Y;
        
return 1;
    }
    
int lastK=32;
    
for(;lastK>m+1;lastK--){
        
if(Y &(1<<(lastK-1))){
            
            
break;
        }
            
    }

    
//its a number st. exp(2,K)
    if(lastK==(m+1)){
        X
=Y;
        
return 1;
    }
    
int k=1<<(m+1);
    
int b=(Y>>m)-(1<<(lastK-m-1));

    X
=(1<<(lastK-m-2))+(b+1-k)/2;

    
if(X<=0){
        k
=k-1-((0-X)<<1);
        X
=0-X+1;
    }
    
    
return k;

}

int main(int argc,char* argv[]){
    
if(argc<=1){
        fprintf(stdout,
"Usage:%s number\n",argv[0]);
        
return 0;
    }
    
int Y=atoi(argv[1]);
    
int X=0;
    
int k=solve(Y,X);
    fprintf(stdout,
"%d=",Y);
    
for(int i=0;i<k;i++){
        fprintf(stdout,
"%d",X+i);
        
if(i<(k-1)){
            fprintf(stdout,
"+");
        }
    }
    fprintf(stdout,
"\n");
    
return 0;
}
posted @ 2011-12-01 22:09 DoubleH 阅读(1765) | 评论 (2)编辑 收藏

     摘要: 年过的差不多了,今天偶尔兴起上HOJ上翻几道DP练手的题来。。。,顺便把代码贴下留念  1.数塔 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->/**  *   */ pack...  阅读全文
posted @ 2011-02-06 21:13 DoubleH 阅读(2012) | 评论 (0)编辑 收藏

     摘要: 前一篇博客,我简单提了下怎么为NIO2增加TransmitFile支持,文件传送吞吐量是一个性能关注点,此外,并发连接数也是重要的关注点。 不过JDK7中又一次做了简单的实现,不支持同时投递多个AcceptEx请求,只支持一次一个,返回后再投递。这样,客户端连接的接受速度必然大打折扣。不知道为什么sun会做这样的实现,WSASend()/WSAReceive()一次只允许一个还是可以理解,...  阅读全文
posted @ 2009-12-04 17:57 DoubleH 阅读(3890) | 评论 (6)编辑 收藏

JDK7的NIO2特性或许是我最期待的,我一直想基于它写一个高性能的Java Http Server.现在这个想法终于可以实施了。
本人基于目前最新的JDK7 b76开发了一个HTTP Server性能确实不错。
在windows平台上NIO2采用AccpetEx来异步接受连接,并且读写全都关联到IOCP完成端口。不仅如此,为了方便开发者使用,连IOCP工作线程都封装好了,你只要提供线程池就OK。

但是要注意,IOCP工作线程的线程池必须是 Fix的,因为你发出的读写请求都关联到相应的线程上,如果线程死了,那读写完成情况是不知道的。

作为一个Http Server,传送文件是必不可少的功能,那一般文件的传送都是要把程序里的buffer拷贝到内核的buffer,由内核发送出去的。windows平台上为这种情况提供了很好的解决方案,使用TransmitFile接口

BOOL TransmitFile(
    SOCKET hSocket,
    HANDLE hFile,
    DWORD nNumberOfBytesToWrite,
    DWORD nNumberOfBytesPerSend,
    LPOVERLAPPED lpOverlapped,
    LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
    DWORD dwFlags
);

你只要把文件句柄发送给内核就行了,内核帮你搞定其余的,真正做到Zero-Copy.
但是很不幸,NIO2里AsynchronousSocketChannel没有提供这样的支持。而为HTTP Server的性能考量,本人只好自己增加这个支持。

要无缝支持,这个必须得表现的跟 Read /Write一样,有完成的通知,通知传送多少数据,等等。

仔细读完sun的IOCP实现以后发现这部分工作他们封装得很好,基本只要往他们的框架里加东西就好了。
为了能访问他们的框架代码,我定义自己的TransmitFile支持类在sun.nio.ch包里,以获得最大的权限。

package sun.nio.ch;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.CompletionHandler;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.WritePendingException;
import java.util.concurrent.Future;


/**

 * 
@author Yvon
 * 
 
*/
public class WindowsTransmitFileSupport {
   
   //Sun's NIO2 channel  implementation class
    
private WindowsAsynchronousSocketChannelImpl channel;
   
    //nio2 framework core data structure
    PendingIoCache ioCache;

   //some field retrieve from sun channel implementation class
    
private Object writeLock;
    
private Field writingF;
    
private Field writeShutdownF;
    
private Field writeKilledF; // f

    WindowsTransmitFileSupport()
    {
        
//dummy one for JNI code
    }

    
/**
     * 
     
*/
    
public WindowsTransmitFileSupport(
            AsynchronousSocketChannel
             channel) {

        
this.channel = (WindowsAsynchronousSocketChannelImpl)channel;
        
try {
        // Initialize the fields
            Field f 
= WindowsAsynchronousSocketChannelImpl.class
                    .getDeclaredField(
"ioCache");
            f.setAccessible(
true);
            ioCache 
= (PendingIoCache) f.get(channel);
            f 
= AsynchronousSocketChannelImpl.class
                    .getDeclaredField(
"writeLock");
            f.setAccessible(
true);
            writeLock 
= f.get(channel);
            writingF 
= AsynchronousSocketChannelImpl.class
                    .getDeclaredField(
"writing");
            writingF.setAccessible(
true);

            writeShutdownF 
= AsynchronousSocketChannelImpl.class
                    .getDeclaredField(
"writeShutdown");
            writeShutdownF.setAccessible(
true);

            writeKilledF 
= AsynchronousSocketChannelImpl.class
                    .getDeclaredField(
"writeKilled");
            writeKilledF.setAccessible(
true);

        } 
catch (NoSuchFieldException e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        } 
catch (SecurityException e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        } 
catch (IllegalArgumentException e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        } 
catch (IllegalAccessException e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    
    
/**
     * Implements the task to initiate a write and the handler to consume the
     * result when the send file completes.
     
*/
    
private class SendFileTask<V, A> implements Runnable, Iocp.ResultHandler {
        
private final PendingFuture<V, A> result;
        
private final long file;//file is windows file HANDLE

        SendFileTask(
long file, PendingFuture<V, A> result) {
            
this.result = result;
            
this.file = file;
        }

    

        @Override
        
// @SuppressWarnings("unchecked")
        public void run() {
            
long overlapped = 0L;
            
boolean pending = false;
            
boolean shutdown = false;

            
try {
                channel.begin();

        

                
// get an OVERLAPPED structure (from the cache or allocate)
                overlapped = ioCache.add(result);
                
int n = transmitFile0(channel.handle, file, overlapped);
                
if (n == IOStatus.UNAVAILABLE) {
                    
// I/O is pending
                    pending = true;
                    
return;
                }
                
if (n == IOStatus.EOF) {
                    
// special case for shutdown output
                    shutdown = true;
                    
throw new ClosedChannelException();
                }
                
// write completed immediately
                throw new InternalError("Write completed immediately");
            } 
catch (Throwable x) {
                
// write failed. Enable writing before releasing waiters.
                channel.enableWriting();
                
if (!shutdown && (x instanceof ClosedChannelException))
                    x 
= new AsynchronousCloseException();
                
if (!(x instanceof IOException))
                    x 
= new IOException(x);
                result.setFailure(x);
            } 
finally {
                
// release resources if I/O not pending
                if (!pending) {
                    
if (overlapped != 0L)
                        ioCache.remove(overlapped);
                
                }
                channel.end();
            }

            
// invoke completion handler
            Invoker.invoke(result);
        }

        

        
/**
         * Executed when the I/O has completed
         
*/
        @Override
        @SuppressWarnings(
"unchecked")
        
public void completed(int bytesTransferred, boolean canInvokeDirect) {
    

            
// release waiters if not already released by timeout
            synchronized (result) {
                
if (result.isDone())
                    
return;
                channel.enableWriting();

                result.setResult((V) Integer.valueOf(bytesTransferred));

            }
            
if (canInvokeDirect) {
                Invoker.invokeUnchecked(result);
            } 
else {
                Invoker.invoke(result);
            }
        }

        @Override
        
public void failed(int error, IOException x) {
            
// return direct buffer to cache if substituted
        

            
// release waiters if not already released by timeout
            if (!channel.isOpen())
                x 
= new AsynchronousCloseException();

            
synchronized (result) {
                
if (result.isDone())
                    
return;
                channel.enableWriting();
                result.setFailure(x);
            }
            Invoker.invoke(result);
        }

    }

    
public <extends Number, A> Future<V> sendFile(long file, A att,
            CompletionHandler
<V, ? super A> handler) {

        
boolean closed = false;
        
if (channel.isOpen()) {
            
if (channel.remoteAddress == null)
                
throw new NotYetConnectedException();

            
            
// check and update state
            synchronized (writeLock) {
                
try{
                
if (writeKilledF.getBoolean(channel))
                    
throw new IllegalStateException(
                            
"Writing not allowed due to timeout or cancellation");
                
if (writingF.getBoolean(channel))
                    
throw new WritePendingException();
                
if (writeShutdownF.getBoolean(channel)) {
                    closed 
= true;
                } 
else {
                    writingF.setBoolean(channel, 
true);
                }
                }
catch(Exception e)
                {
                    IllegalStateException ise
=new IllegalStateException(" catch exception when write");
                    ise.initCause(e);
                    
throw ise;
                }
            }
        } 
else {
            closed 
= true;
        }

        
// channel is closed or shutdown for write
        if (closed) {
            Throwable e 
= new ClosedChannelException();
            
if (handler == null)
                
return CompletedFuture.withFailure(e);
            Invoker.invoke(channel, handler, att, 
null, e);
            
return null;
        }



        
return implSendFile(file,att,handler);
    }


    
<extends Number, A> Future<V> implSendFile(long file, A attachment,
            CompletionHandler
<V, ? super A> handler) {
        
// setup task
        PendingFuture<V, A> result = new PendingFuture<V, A>(channel, handler,
                attachment);
        SendFileTask
<V,A> sendTask=new SendFileTask<V,A>(file,result);
        result.setContext(sendTask);
        
// initiate I/O (can only be done from thread in thread pool)
        
// initiate I/O
        if (Iocp.supportsThreadAgnosticIo()) {
            sendTask.run();
        } 
else {
            Invoker.invokeOnThreadInThreadPool(channel, sendTask);
        }
        
return result;
    }
    
    
private native int transmitFile0(long handle, long file,
            
long overlapped);
    
}

这个操作跟默认实现的里的write操作是很像的,只是最后调用的本地方法不一样。。

接下来,我们怎么使用呢,这个类是定义在sun的包里的,直接用的话,会报IllegalAccessError,因为我们的类加载器跟初始化加载器是不一样的。
解决办法一个是通过启动参数-Xbootclasspath,让我们的包被初始加载器加载。我个人不喜欢这种办法,所以就采用JNI来定义我们的windows TransmitFile支持类。

这样我们的工作算是完成了,注意,发送文件的时候传得是文件句柄,这样做的好处是你可以更好的控制,一般是在发送前,打开文件句柄,完成后在回调通知方法里关闭文件句柄。



有兴趣的同学可以看看我的HTTP server项目:
http://code.google.com/p/jabhttpd/

目前基本功能实现得差不多,做了些简单的测试,性能比较满意。这个服务器不打算支持servlet api,基本是专门给做基于长连接模式通信的定做的。






posted @ 2009-11-29 15:19 DoubleH 阅读(2589) | 评论 (2)编辑 收藏

问题:
有个链表(List),有N个元素,当N很大的时候,我们通常想分批处理该链表。假如每次处理M条(0<M<=N),那么需要处理几次才能处理完所有数据呢?

问题很简单,我们需要<N/M>次,这里我们用<>表示向上取整,[]表示向下取整,那么怎么来表示这个值呢?
我们可以证明:
<N/M>=[(N-1)/M]+1    (0<M<=N,M,N∈Z)

不失一般性,我们设N=Mk+r(0<=r<M),
1)当r>0时,

左边:<N/M>=<(Mk+r)/M>=<k+r/M>=k+<r/M>=k+1
右边:[(N-1)/M]+1=[(Mk+r-1)/M]+1=[k+(r-1)/M]+1=k+1+[(r-1)/M]=k+1
2)当r=0
左边:<N/M>=k
右边:[(N-1)/M]+1=[(Mk-1)/M]+1=[(M(k-1)+M-1)/M]+1=[k-1+(M-1)/M]+1=k+[(M-1)/M]=k

命题得证。

有了这个公式,我们在Java代码里可以这样计算:
int nn=(N-1)/+1
.


因为'/'是往下取整的。








posted @ 2009-05-04 11:45 DoubleH 阅读(3958) | 评论 (4)编辑 收藏

原题:

Command Network

Description

After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.

With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node A to another node B, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.

Input

The input contains several test cases. Each test case starts with a line containing two integer N (N ≤ 100), the number of nodes in the destroyed network, and M (M ≤ 104), the number of pairs of nodes between which a wire can be built. The next N lines each contain an ordered pair xi and yi, giving the Cartesian coordinates of the nodes. Then follow M lines each containing two integers i and j between 1 and N (inclusive) meaning a wire can be built between node i and node j for unidirectional command delivery from the former to the latter. littleken’s headquarter is always located at node 1. Process to end of file.

Output

For each test case, output exactly one line containing the shortest total length of wires to two digits past the decimal point. In the cases that such a network does not exist, just output ‘poor snoopy’.


一开始没仔细读题,一看以为是最小生成树呢,结果Krusal算法上去WA了,Prim算法也WA,修修改改一直WA,第二天发现本题是要在有向图上面构造最小树形图。

按照著名的Zhu-Liu算法,仔细实现了一边,终于AC了。
按照我的理解总结下该算法,该算法对每个结点,除根节点外寻找最小入边,
1)如果这些入边不构成环,那么容易证明这些边构成最小树形图。
证明:设加上根节点r一共N个点,那么一共有N-1条边,证明从r能到每个点,若存在一点v,使得从r到v没有路径,那么,假设从v反向回退必然构成环,因为每个点除了r都有入边,如果不构成环,该路径可以无穷大。
2)如果存在环,我们把环收缩成一个点,更新相应的入边和出边,得到新的图G',使得原问题在G'中等效:
怎么收缩呢?
假设我们把环收缩成环上的任意一点v,所有进环的边和出环的边自动变成v的边(如果已有,取长度最短的),其余点标记为删除,更新不在环上的所有点进入该环的长度cost为cost-cost(prev[x],x);其中点x为进入环的边在环上的端点。出边保持不变。

这里为什么这么更新?因为这样更新使得我们的算法在新图上是等效的。任何环的解决后意味着在新图里面得为改收缩后的点寻找新的入边,而实际的花费应该是新的入边减去原有的入边长度,我们的算法在找到环的时候就把环上所有的边的长度计算在花费内了.而对出边是没有影响的。


到这算法的框架基本出来了。当为某点没找到入边的时候,意味着无解。为了加快无解的侦测,我们先运行一遍DFS搜索,假如从根节点出发,可触及的节点数小于N-1(不含r)则意味着无解。反之,肯定有解。
为什么?
因为如果可触及数小于N-1,意味着某点是不可触及的,也就是原图不是弱连通。对该点来说不存在从r到它的路径。反之,从r到某点都有一条路径,沿着该路径就能找到结点的入边。


第二个问题是,如何快速侦测环呢?
我使用了一个不相交集。回忆Krusal的算法实现里面也是使用不相交集来避免找产生环的最小边。

下面是我的代码:

// 3164.cpp : Defines the entry point for the console application.
//

#include 
<iostream>
#include 
<cmath>


using namespace std;

typedef 
struct _Point
{

    
double x;
    
double y;

    
double distanceTo(const struct _Point& r)
    {
          
return sqrt((x-r.x)*(x-r.x)+(y-r.y)*(y-r.y));

    }

}Point;



const int MAX_V=100;
const int MAX_E=10000;
const double NO_EDGE=1.7976931348623158e+308;

Point vertexes[MAX_V]
={0};
int parents[MAX_V]={0};
int ranks[MAX_V]={0};
double G[MAX_V][MAX_V]={0};
bool visited[MAX_V]={0};
bool deleted[MAX_V]={0};
int prev[MAX_V]={0};

int nVertex=0;
int nEdge=0;




int u_find(int a)
{
    
if(parents[a]==a)return a;
    parents[a]
=u_find(parents[a]);
    
return parents[a];
}
void u_union(int a,int b)
{
    
int pa=u_find(a);
    
int pb=u_find(b);
    
if(ranks[pa]==ranks[pb])
    {
        ranks[pa]
++;
        parents[pb]
=pa;
    }
else if(ranks[pa]<ranks[pb])
    {
        parents[pa]
=pb;
    }
    
else
    {
        parents[pb]
=pa;
    }
}

void DFS(int v,int& c)
{

    visited[v]
=true;
    
for(int i=1;i<nVertex;i++)
    {
        
if(!visited[i]&&G[v][i]<NO_EDGE)
        {
            c
+=1;

            DFS(i,c);
        }

    }

}



void doCycle(int s,int t,double& cost)
{
    memset(visited,
0,sizeof(bool)*nVertex);
    
int i=s;
    
do
    {
        visited[i]
=true;
        cost
+=G[prev[i]-1][i];
        
//cout<<"from "<<(prev[i]-1)<<" to "<<i<<" (cycle)"<<" weight:"<<G[prev[i]-1][i]<<endl;
        i=prev[i]-1;

    }
while(i!=s);


    
do
    {
        

        
for(int k=0;k<nVertex;k++)
        {
            
if(!deleted[k]&&!visited[k])
            {
            
                
if(G[k][i]<NO_EDGE)
                {
                    
if(G[k][i]-G[prev[i]-1][i]<G[k][s])
                    {


                        G[k][s]
=G[k][i]-G[prev[i]-1][i];
                        
//cout<<"1.update ["<<k<<","<<s<<"] at "<<i<<" as "<<G[k][s]<<endl;
                    }
                    

                }
                
if(G[i][k]<NO_EDGE)
                {
                    
if(G[i][k]<G[s][k])
                    {

                        G[s][k]
=G[i][k];
                        
//cout<<"2.update ["<<s<<","<<k<<"] at "<<i<<" as "<<G[s][k]<<endl;
                    }
                    
                }
            }
        }


        
if(i!=s)
        {
            deleted[i]
=true;
            
//cout<<"mark "<<i<<" as deleted"<<endl;
        }
        i
=prev[i]-1;


    }
while(i!=s);



}




int main(void)
{



    
    
while(cin>>nVertex>>nEdge)
    {

        
int s,t;

        
int nv=0;
        
bool cycle=0;
        
double cost=0;
        memset(vertexes,
0,sizeof(vertexes));
        memset(visited,
0,sizeof(visited) );

        memset(deleted,
0,sizeof(deleted));
        memset(G,
0,sizeof(G));
        memset(prev,
0,sizeof(prev));


        memset(ranks,
0,sizeof(ranks));

        memset(parents,
0,sizeof(parents));

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

            cin
>>vertexes[i].x>>vertexes[i].y;
            parents[i]
=i;
            
for(int j=0;j<nVertex;j++)
            {
                G[i][j]
=NO_EDGE;
            }

        }
        
for(int i=0;i<nEdge;i++)
        {

            cin
>>s>>t;
            
if(t==1||s==t)continue;
            G[s
-1][t-1]=vertexes[s-1].distanceTo(vertexes[t-1]);

        }



        DFS(
0,nv);
        
if(nv<nVertex-1)
        {

            cout
<<"poor snoopy"<<endl;
            
continue;
        }



        
do {
            cycle
=false;
            
for(int i=0;i<nVertex;i++){parents[i]=i;}
            memset(ranks,
0,sizeof(bool)*nVertex);
        

            
for (int i = 1; i < nVertex; i++) {
                
double minimum=NO_EDGE;
                
if(deleted[i])continue;
                
for(int k=0;k<nVertex;k++)
                {
                    
if(!deleted[k]&&minimum>G[k][i])
                    {
                        prev[i]
=k+1;
                        minimum
=G[k][i];
                    }
                }
                
if(minimum==NO_EDGE)
                {
                

                    
throw 1;
                }
                
if (u_find(prev[i]-1== u_find(i)) {
                    doCycle(prev[i]
-1,i, cost);
                    cycle 
= true;
                    
break;
                }
                
else
                {
                    u_union(i,prev[i]
-1);
                }

            }


        } 
while (cycle);

        
for (int i = 1; i < nVertex; i++) {
            
if(!deleted[i])
            {
                cost
+=G[prev[i]-1][i];
                
//cout<<"from "<<(prev[i]-1)<<" to "<<i<<" weight:"<<G[prev[i]-1][i]<<endl;
            }

        }

        printf(
"%.2f\n",cost);


    }


}



posted @ 2009-04-24 16:39 DoubleH 阅读(2416) | 评论 (0)编辑 收藏

   今天买了本《算法概论》影印注释版,仔细看了第一章,果然名不虚传,很是吸引人。
第一章的习题难度适中,这里抽出第35题来,这题是证明Wilson定理。
Wilson定理:
   N是一个素数当且仅当: (N-1)! ≡ -1(mod N)

证明:
  首先我们证明若N是素数,那么等式成立,对于N=2,这是很明显的。以下证明N>2 的情形。
  1)若N是素数,那么关于N同余的乘法群G={1,2,3....N-1}
    群G每个元素都有逆元,映射 f:a -> a^-1 ,f(a)=a^-1 是一个一一映射。现在,任取一个元素,它的逆元要么是其它一个元素,或者是它本身。 我们假设其中元素x的逆元是它本身,那么x*x ≡1(mod N) =>(x+1)*(x-1)=K*N,而N是素数,所以要么x=N-1,要么x=1。也就是说,除了这两个元素,其它的元素的逆元都是映射到别的元素的。而N>2,是奇数,所以元素共有N-1个,也就是偶数个元素。这样,除了1和N-1外剩余的N-3个元素刚好结成两两一对(x,y)(逆元是唯一的)使得f(x)=y=x^-1,也就是xy≡1(mod N).
现在把G的元素全部乘起来,让互为逆元的元素组成一对那么(N-1)!=1*(N-1)*(x1*y1)*(x2*y2)...(xk*yk)≡1*(N-1)*1*1...1 (mod N)≡-1(mod N).
    这样,我们证明了一个方向了,下面我们证明另一个方向

  2)若(N-1)! ≡ -1(mod N)成立,则N是素数,若不然,令 N是和数。则(N-1)!肯定整除N,因为N的每个因子p满足p>1,p<N。
   现在令(N-1)!=K1*N.又(N-1)! ≡ -1(mod N) => K1*N ≡ -1(mod N),由同余性质知,存在K2使得K1*N+1=K2*N,两边同时除以N得K1+1/N=K2.显然,这是不可能的,所以若(N-1)! ≡ -1(mod N)成立,则N是素数

证明完毕!

这里用群的概念能够简化一定的描述,其实可以完全不用群的概念的,只不过这样一来,描述更长点,繁琐点!





posted @ 2009-04-10 22:38 DoubleH 阅读(2016) | 评论 (1)编辑 收藏