sub pick()
Dim a(99) As Integer
Dim x As Integer
    
For i = 0 To 99
   a(i) = i
Next i
For i = 400 To 0 Step -1
   x = Int(i * Rnd)
   debug.print a(x)
  '关键看下面,从数组中移去已经选出的项目,就再也选不到了
   For j = x To i - 1
       a(j) = a(j + 1)
   Next j
Next i
end sub
唉,非常佩服
大家要是还有这样巧妙的代码也别吝啬,一起贴出来共享之。

解决方案 »

  1.   

    For i = 400 To 0 Step -1
    为什么设为400,不是太明白
      

  2.   

    sub pick()
    Dim a(99) As Integer
    Dim x As Integer
    Dim i as long
    Dim j as long
        
    For i = 0 To 99
       a(i) = i
    Next i
    For i = 99 To 0 Step -1
       x = Int(i * Rnd)
       debug.print a(x)
      '关键看下面,从数组中移去已经选出的项目,就再也选不到了
       For j = x To i - 1
           a(j) = a(j + 1)
       Next 
    Next 
    end sub
    不好意思,应该是这个。哈哈
      

  3.   

    '关键看下面,从数组中移去已经选出的项目,就再也选不到了
       For j = x To i - 1
           a(j) = a(j + 1)
       Next 
    这个还是没看太懂??还望大哥指点?不知道和上面是什么关系?
      

  4.   

    經過多次測試,確實如樓主所說,沒有重復的記錄
    不過
       For j = x To i - 1
           a(j) = a(j + 1)
       Next 
    在其中所起的作用還是不太明白,還請樓主指點指點
      

  5.   

    剛想打個旽就想到是怎麼回事了
       For j = x To i - 1
           a(j) = a(j + 1)
       Next 
    的作用就是把剛剛找出的a(x)從數組中刪除,沒有了看你怎麼重復不知對否?
      

  6.   

    这个只是小仙妹的跳蚤算法的一种变形,很久以前的事了,好像后来她(他?)还搞了其他几套适用于不同情况的跳蚤算法变形。
    有兴趣的联系一下小仙妹吧,还有其他几种巧妙有趣又实用的算法,象什么“小仙妹张牙舞爪抓羊羔算法”用来解决围圈选数问题,“立方柱统计算法”用来解决统计问题(例如图形的色彩分布)之类方法都很简单,但效率高最让人佩服的是她(他?)解决问题的奇思妙

    不过最近那家伙好像在玩游戏了(好像叫战神吧),不时在水源看见她
    csdn好像不能根据回复者的姓名搜索可惜
      

  7.   

    好,看看小仙妹自己说几句大家鼓掌欢迎:回复人: KiteGirl(小仙妹) ( ) 信誉:100  2004-05-18 05:05:00  得分:0  
     下面是关于“跳蚤算法”的一些改进,如果你取的值范围比较宽,数量又比较多,则要注意看看了。最原始的跳蚤算法,如果你要在10到100之内取值,则需要定义一个91个元素的数组,存储10到100这91个数字。原始的跳蚤算法有两个缺点:
    1、对相对范围(最大值与最小值的差)有限制。如果你要取100个从1到10000000000的不重复随机数,则需要定义10G容量的数组,这是非常恐怖的事情。2、即使你要从1到100之间取10个数字,但原始的跳蚤算法在一开始要将100个元素全部赋初始值。这样在一定程度上影响速度。解决方法:1、解决相对范围限制,采用的方法是以虚拟数组代替真实数组。虚拟数组当中,只有你访问到的元素才真实存在。每当你访问一个元素的时候,程序检索该元素是否存在。如果不存在则添加,如果存在则引用。
    优点:解决了取值范围限制。
    缺点:降低了速度。2、动态初始化。数组有一个“底表”,记录该数组是否被访问过。如果没有访问过,第一次访问赋予初始值。
    优点:在取值数量明显小于取值范围的情况下,效率比较高。
    缺点:多消耗至少1/8的内存空间(实际消耗是一倍)。 
      

  8.   

    再来:回复人: KiteGirl(小仙妹) ( ) 信誉:100  2004-05-06 23:26:00  得分:0  
     
     
      不重复随机数可以用我的看家本领“跳蚤算法”,前提是这个随机数的取值是确定,并且范围不大的。其原理是这样的:
    1、建立一个数组,该数组的大小取决于取随机不重复序列的最大值。比如你要取0-100之间的数字,则需要建立101个元素(0-100)的数组。2、为数组的每个元素设置初始值,初始值就是该元素的索引。3、将前N个元素与第R个元素交换。0<=N<=元素的数量。R为随机数,0<=R<=元素的数量4、前N个元素就是随机不重复序列。该算法是我根据用彩球取彩票号码的情景构思出来的。由于工作的时候,每个元素的值好象跳蚤一样到处转移,因此叫做“跳蚤算法”。学名似乎应该叫“有限序列交换法”比较恰当。
    该算法的优点是在时间上比较迅速稳定。
    该算法的缺点主要是:数组的绝对差不能太大。比如:你要在0-1000000000000之间取N个不重复序列、或者取0.000000000001-0.1之间的小数。该算法就难以满足了。但1000000000000-1000000010000之间的整数、位数比较少的小数还是可以的。(小数采用乘法放大)取12个0-99以内的随机不重复正整数序列:Dim tNumbers() As Long
    Dim tMax As Long
    Dim tIndex As Long
    Dim tSwapIndex As Long
    Dim tT As LongtMax=99ReDim tNumbers(tMax)For tIndex=0 To 99
      tNumbers(tIndex)=tIndex
    NextFor tIndex=0 To 11
      tSwapIndex=Int(Rnd*(tMax+1))'确定交换目标
      '将当前元素与随机元素交换。
      tT=tNumbers(tIndex)
      tNumbers(tIndex)=tNumbers(tSwapIndex)
      tNumbers(tSwapIndex)=tT
    NexttNumbers()的0-11这12个元素必然是随机不重复序列。 
    csdn好像不能连发三贴,如果有人还想看的请顶贴
      

  9.   

    of123() ( ) 信誉:125    Blog  2006-08-30 12:48:00  得分: 0  
     
     
       美中不足,最后一个元素必然是最后被选到,不怎么随机。
      
     
    =====================
    不会的,如果按跳蚤算法原意,“最后一个元素”也有可能第一个被选出来的,楼主贴出来的那段代码其实并不好。。
      

  10.   

    For j = x To i - 1
           a(j) = a(j + 1)
       Next 有必要这么麻烦吗, 直接把 a(x)和a(i)交换下就行了
    反正是随机, 每次把取出来的数放到屁股后面去, 然后 rnd的范围在少1不就行了。
    那个循环移动效率多慢。
      

  11.   

    Sub pick()
    Dim i As Long, j As Long
    Dim a(99) As Integer
    Dim x As Integer
        
    For i = 0 To 99
       a(i) = i
    Next i
    For i = 99 To 0 Step -1
       x = Int((i + 1) * Rnd)
       Debug.Print a(x)   Dim tmp As Long
       tmp = a(x)
       a(x) = a(i)
       a(i) = tmpNext i
    End Sub这样就行了,  还有
    应该是
    x = Int((i + 1) * Rnd) 才是取 0~99
      

  12.   

    pigsanddogs(我爱吃猪肉,但是长不胖,为什么??) ( ) 信誉:110    Blog  2006-08-30 13:44:00  得分: 0  
     
     
       For j = x To i - 1
           a(j) = a(j + 1)
       Next 有必要这么麻烦吗, 直接把 a(x)和a(i)交换下就行了
    反正是随机, 每次把取出来的数放到屁股后面去, 然后 rnd的范围在少1不就行了。
    那个循环移动效率多慢。
    =====================
    就是就是楼主贴出来那段代码如果取值范围稍微大一点恐怕就不行了  
     
      

  13.   

    哈哈,pigsanddogs(我爱吃猪肉,但是长不胖,为什么??) 说得真好,直接和最后一个交换就可以了,反正也不会选择到了。
    这样的话比较大的数组也不会慢了。这个名字有点象某人,不知道是否马甲。
      

  14.   

    期待楼上zzwu(未名) 拿出不同的例子来
      

  15.   

    sub pick()
    Dim a(99) As Integer
    Dim x As Integer
    Dim i as long
    Dim j as long
    Dim y as integer    
    For i = 0 To 99
       a(i) = i
    Next i
    For i = 99 To 0 Step -1
       x = Int(i * Rnd)
       debug.print a(x)
       y=a(x)
      '关键看下面,从数组中移去已经选出的项目,就再也选不到了
       a(x)=a(i)
       a(i)=y
    Next 
    end sub
      

  16.   

    这段程序的精髓在x = Int(i * Rnd)中的i的应用
      

  17.   

    For j = x To i - 1
           a(j) = a(j + 1)
       Next 有必要这么麻烦吗, 直接把 a(x)和a(i)交换下就行了
    反正是随机, 每次把取出来的数放到屁股后面去, 然后 rnd的范围在少1不就行了。
    那个循环移动效率多慢。
    ------------------------------------------------------------------------
    是的啊,为了干掉选中的那个值而把它后面所有的元素向前移动,付出的代价太大了。
      

  18.   

    弱弱的问一声:能写个C#的吗?没学过VB
      

  19.   

    x = Int((i + 1) * Rnd) 才是取 0~99   ???
      

  20.   

    我综合楼上几位的意见,改动了一下,改进入下:1:不需要再移动数组元素,提高速度
    2:最后元素也是可以随机选取:窗体上放个按钮,贴以下代码,经过我多次测试没有发现上述问题:Private Sub Command1_Click()
    Dim A(9) As Long
    Dim X As Long
    Dim I As Long
    Me.Cls
    For I = 0 To 9
       A(I) = I
    Next I
    For I = 9 To 0 Step -1
       X = Int(I * Rnd)
       Me.Print A(X)
       A(X) = A(I)
    Next
    End Sub
      

  21.   

    我见过的类似的代码:long int * generate_random_permutation( long int n )
    /*    
          FUNCTION:       generate a random permutation of the integers 0 .. n-1
          INPUT:          length of the array
          OUTPUT:         pointer to the random permutation
          (SIDE)EFFECTS:  the array holding the random permutation is allocated in this 
                          function. Don't forget to free again the memory!
          COMMENTS:       only needed by the local search procedures
    */
    {
       long int  i, help, node, tot_assigned = 0;
       double    rnd;
       long int  *r;   r = malloc(n * sizeof(int));     for ( i = 0 ; i < n; i++) 
         r[i] = i;   for ( i = 0 ; i < n ; i++ ) {
         /* find (randomly) an index for a free unit */ 
         rnd  = ran01 ( &seed );
         node = (long int) (rnd  * (n - tot_assigned)); 
         assert( i + node < n );
         help = r[i];
         r[i] = r[i+node];
         r[i+node] = help;
         tot_assigned++;
       }
       return r;
    }
      

  22.   

    TO Eleve() 这里可没有用到什么VB特有的东西,我想可能除了哪个RND的写法不同之外,就再也找不出不能翻译成C#的理由了。呵呵
      

  23.   

    WallesCai(沧海明月一度,西风残阳无悔.) ( ) 信誉:107    Blog  2006-08-30 14:59:00  得分: 0  
     
     
       我综合楼上几位的意见,改动了一下,改进入下:1:不需要再移动数组元素,提高速度
    2:最后元素也是可以随机选取:窗体上放个按钮,贴以下代码,经过我多次测试没有发现上述问题:Private Sub Command1_Click()
    Dim A(9) As Long
    Dim X As Long
    Dim I As Long
    Me.Cls
    For I = 0 To 9
       A(I) = I
    Next I
    For I = 9 To 0 Step -1
       X = Int(I * Rnd)
       Me.Print A(X)
       A(X) = A(I)
    Next
    End Sub
    ===============================
    很明显,这代码有问题。。
      
     
      

  24.   

    TO zzwu(未名)
    看看我最后贴的代码,没有用到移动数组元素。还有,VB中没有直接的方法使用链表。不能象C中那样通过修改指针来变更链表。
    并且,当数据很多的时候,链表的查找速度也是比较慢的。(我记忆中是要按照数据结构中的后续或前续地址去寻址下一个的)并不见得快到哪里去。
      

  25.   

    tSwapIndex=Int(Rnd*(tMax+1))
    'Int(Rnd*(tMax+1))还是可能重复的
      

  26.   


    For I = 9 To 0 Step -1
       X = Int(I * Rnd)
       Me.Print A(X)
       A(X) = A(I)
    Next
    ===============rnd  是半开区间。 所以要加1
    不要被表面所迷惑, 因为那个9被移走了, 所以你看到不是最后一个, 就以为随机了
    代码中加入
    randomize timer
    rand(-1)
    然后执行100次,看有一次9能到第一个出现吗。
      

  27.   

    TO lsftest() 
    不知道你所说的问题是否和zzjlxla() 说的一样,破坏了原来的数组呢?
      

  28.   

    tSwapIndex=Int(Rnd*(tMax+1))
    'Int(Rnd*(tMax+1))还是可能重复的
    ==================
    那个值只是数组的索引号,重复也是无所谓的,只要里面的值不重复就行了。
    另外,那段代码数组里的每一个元素都有可能不止交换一次,从另一个角度来说,似乎随机性更强一点虽然这不是必须的
      

  29.   

    TO lsftest() 
    不知道你所说的问题是否和zzjlxla() 说的一样,破坏了原来的数组呢?
    ===============
    是的,pigsanddogs(我爱吃猪肉,但是长不胖,为什么??)兄说的问题倒没有留意 。
      

  30.   

    pigsanddogs(我爱吃猪肉,但是长不胖,为什么??) 兄说得对,如果:
    For I = 9 To 0 Step -1
       X = Int(I * Rnd)
       Me.Print A(X)
       A(X) = A(I)
    Next
    则所有元素的值都不可能在它原来的位置里出现,并不是真正的随机。
      

  31.   

    TO lsftest() 
    先前我也留意到了这个问题,不过区别在于,我并没有把这个当做问题,因为讨论的主题是随机选取。 
    因此我最后写的那一段代码只是“一次性”的,也因此必须在开始的时候对数组重新初始化:重新附值,或者利用另外一个数组来记录下初试状态,在选择完毕后再用它来恢复这个数组(不过这倒是很方便的,一个COPYMEMORY就可以搞定,速度依然很快。)不过这个问题,从上面所有人的回答中都没有谈到,也就是说,到现在为止上面所有朋友回答的方法都会破坏原来的数据排列(包括用数据链表的)
    TO pigsanddogs(我爱吃猪肉,但是长不胖,为什么??) 
    不是非常理解你的话(我是说原话的意思)。
    我前面说“2:最后元素也是可以随机选取”,这句话并不严密,因为它还是不能在第一次被选中,它只是“不会只能在最后一次被选中”
    我想你说的就是这个意思吧??
      

  32.   

    TO lsftest() 
    通过你的话,我有点理解了 pigsanddogs(我爱吃猪肉,但是长不胖,为什么??) 的话。但是请大家注意一个问题: 随机有顺序么?
    因为被选中的数组元素的值被最后一个数组元素的值代替了,因此原有的顺序被打乱,但是这依然是随机,它只是不是由RND所产生的那个随机了而已。并且因为这个随机还包含了前几次随机所改变的顺序,因此反而更随机了。
    就象熵一样,熵的熵,它依然是熵。(这里我们有一个前提,就是假设计算机给我们的确实是一个随机数。哪怕它只是一个伪随机数,只要在我们可以达到的测试次数内不重复,我就认为它是真的随机数。)
      

  33.   

    根据以前看到的文章我想到另一种方法
    从数据库中取不重复数据^_^~shuliang = 1000
    sql="INSERT INTO "&ku2&" SELECT TOP "&shuliang&" unid FROM "&ku1&" WHERE (unid NOT IN (SELECT unid FROM "&ku2&")) ORDER BY NEWID()"我们只需要将需要提取的所有数据导入ku1中,然后执行以上sql语句,挑选出的不重复数据写入ku2中,然后导出ku2中的数据就行了适合数据量很大,并且提供的原始数据有一定格式并可以导入数据库由于导入导出操作通过sql server的dts组件完成,速度很快
      

  34.   

    并不是说随机要有顺序,如果有顺序的就不是真正的随机了。
    而是说那段代码缩小了随机的范围,一些应该有可能出现的序列用那段代码根本不可能出现问题出在:
    X = Int(I * Rnd)
    应该改为:X = Int((I+1) * Rnd)
      

  35.   

    首先要指正的是在函数中缺少 “Randomiz”语句;
    这个函数的确不错,我第一眼看了非常眼熟,与我以前写过的“从N个数中随机取出M个数”的算法非常像。(典型的应用如彩票中35选7)
      

  36.   

    TO lsftest() 兄不需要运行,我知道你指出的问题,如前所说,这确实是个一次性的代码。
    不过无论是打乱次序也好,破坏内容也好,都是对原来数组的破坏,其性质没有什么不同。如果是把找到的数组元素和最后一个数组元素交换值的唯一好处就是可以保留这一次的随机记录(只要把运行之后的数组按照从后到前的次序取出值,就是运行时得到的随机序列了)我说得对么?
      

  37.   

    TO zzwu(未名)
    你说的办法可行,但是要做到全部打乱数组顺序,最少也需要访问一半的数组元素(我说的是最少)。但是也很有可能需要全遍历数组,这样的话结合交换数组元素所用到的步骤,就不见得快了。TO zxc_999()
    有问题就提出来,大家都会帮你的(只要你放分,呵呵)
      

  38.   

    你介绍的算法有双重循环,时间复杂性是o(n*n),
    而用洗牌法则为o(k*n),其中k是不依赖n的,例如,
    将两付104张纸牌洗牌,k有4-5次就够乱了.
      

  39.   

    举个例子解释:原数据如下:
    0 1 2 3 4 5 6 7 8 9   此时i = 9假设x = Int(i * Rnd)中 x == 4,那么4将被选出 (随机数不会产生比9大的)
    此时将从4以后的数字向前移一位后,数字变成成了
    0 1 2 3 5 6 7 8 9 9  但因为此时i等于8,所以可供选择的数字只有0 1 2 3 5 6 7 8 9 
      

  40.   

    TO zzwu(未名)
    你可能漏掉了我中间的回贴没有看,代码如下:Private Sub Command1_Click()
    Dim A(9) As Long
    Dim X As Long
    Dim I As Long
    Me.Cls
    For I = 0 To 9
       A(I) = I
    Next I
    For I = 9 To 0 Step -1
       X = Int(I * Rnd)
       Me.Print A(X)
       A(X) = A(I)
    Next
    End Sub只有一次循环
      

  41.   

    To lsftest() 
    OK, 就如你举的例子,为了不让坐在位子上等待挑选的几位兄弟牺牲掉,偶把代码改成:
    Private Sub Command1_Click()
    Dim A(9) As Long
    Dim X As Long
    Dim I As Long
    Dim L As Long
    Me.Cls
    For I = 0 To 9
       A(I) = I
    Next I
    For I = 9 To 0 Step -1
       X = Int(I * Rnd)
       Me.Print A(X)
       L=A(X)
       A(X) = A(I)
       A(I)=L
    Next
    End Sub这下好了,总算没有人牺牲了。不过呢,除非你愿意每个位置挨个找过去,否则你还是找不到原来坐在那里的哥们儿,在我看来,这和牺牲了也差不多。
    因为在数组中按数组索引找到数组元素的内容是正常的用法,而现在你只能说“我知道在那边一万个人里面有一个人叫某某某,不过我不知道他的具体位置”。当你事先知道某个未排序数组元素的值,反过来查找该值在数组中的位置,那可真没多大用处。
      

  42.   

    banmuhuangci(待缘)有弊端,最后一个永远是99=====================================为什么最后一个永远是99呢?第一个肯定是有可能随机到99的吧?
    我想应该是概率的问题,而并非一定最后一个都是99
      

  43.   

    偶是菜鸟,想问下
    For i = 400 To 0 Step -1
       x = Int(i * Rnd)
       debug.print a(x)这个400是怎样取的?