生成大小为X的随机数的算法

时间:2011-08-24 08:04:09

标签: java algorithm math random

在我的移动应用程序中,我必须为用户提供随机的唯一X字母数字代码,以便用户可以使用该字母数字代码进行回复以执行某项任务。

使用此应用程序的用户数量约为100万人,邮件流量约为每天10万条消息。

我只能使用26个大写字母,26个低位字母和10个数字。如果随机数大小为5,那么我可以生成916132832个唯一组合。在组合耗尽之后,我想再次回收这个数字代。

我正在寻找一种算法方法。是否有任何算法方法可以解决这个问题?

11 个答案:

答案 0 :(得分:6)

如果您接受回收随机数,为什么要在回收之前等待组合耗尽?

  • 这使得数字在到达组合集
  • 的末尾时越来越随机
  • 这会强制您维护一些数据库,以了解哪些号码已被使用,哪些号码尚未使用。

我会生成随机数,如果它们已经被使用过,就不用关心。

如果你真的想像你问的那样保留它,那么你可以这样做:

  • 生成所有组合并将它们放入数据库表
  • 将此表的大小存储在某个变量中
  • 生成1和表格大小之间的随机数R
  • 获取存储在表格的第R行的组合
  • 从表中删除Rth行,并减小size变量
  • 当表为空(并且size变量为0)时,再次启动

您可以通过将使用过的数字从一个表移动到另一个表来改进它,并在第一个表为空时使用第二个表而不是第一个表。

如果你有足够的内容,你也可以在记忆中这样做。

答案 1 :(得分:3)

您的代码可以是唯一的,也可以是算法生成的。

据我所知,您会想到以这种方式将序数映射到代码中的算法,每个数字< =所有可能的代码计数将映射到可预测的代码中。然而,它不是随机的,但对于不知道算法的用户来说似乎是随机的。

否则你必须记住所有使用代码,这在技术上是不可实现的。

答案 2 :(得分:3)

执行此操作的最佳方法是使用名为Format Preserving Encryption或FPE的加密技巧。您的问题与FPE的this application非常相似。使用Feistel network method生成FPE可以最好地解决您的问题。在您的具体情况下,916132832约为2 29.7 ,因此您应该使用30位宽的Feistel网络以及自行车行走。

您选择随机密钥AES密钥K并维护此K以及计数器C.C从0开始,每次分发代码时增加1。代码是C的FPE加密。当C达到916132832时,您已经用完了所有代码。您现在可以选择另一个AES密钥K',设置C = 0,然后重新开始。您可能需要根据应用程序保存所有未确认的(K,C)对。您可能希望在这些未确认的对上设置到期日期,以便减少存储需求。

答案 3 :(得分:2)

使用5个字符,您可以安全地使用900天,然后必须重置。

几周前我为另一个StackOverflow用户编写了一些代码。它是一个随机生成器,只生成新数字。

import java.util.BitSet;
import java.util.Random;

/**
 * Random number generator without repeat in the given range at construction time.
 * 
 * @author martijn
 */
public class NoRepeatRandom {

    private Random random;
    private BitSet used;
    private int max;

    /**
     * Creates new instance of NoRepeatRandom, with the range <code>[0-max[</code>.
     * @param max the maximum for the range
     * @param seed the seed for the underlying {@link java.util.Random}
     */
    public NoRepeatRandom(int max, long seed)
    {
        this.max = max;
        this.used = new BitSet(max);
        this.random = new Random(seed);
    }

    /**
     * Creates new instance of NoRepeatRandom, with the range <code>[0-max[</code>.<br />
     * <code>System.currentTimeMillis()</code> is used as seed for the underlying {@link java.util.Random}
     * @param max the maximum for the range
     */
    public NoRepeatRandom(int max)
    {
        this(max, System.currentTimeMillis());
    }

    /**
     * Gives the next random number
     * @return a new random number. When finished it returns -1.
     */
    public int next()
    {
        if (isFinished())
        {
            return -1;
        }
        while (true)
        {
            int r = random.nextInt(max);
            if (!used.get(r))
            {
                used.set(r);
                return r;
            }
        }
    }

    /**
     * Tells if the random generator has finished. Which means that all number in the range
     * [0-max[ are used.
     * @return true if all numbers are used, otherwise false.
     */
    public boolean isFinished()
    {
        return max == used.cardinality();
    }

    /**
     * Sets all the numbers in the range [0-max[ to unused. Which means that all the numbers
     * can be reused.
     */
    public void reset()
    {
        used.clear();
    }

    /**
     * 
     * @return the maximum.
     */
    public int getMax()
    {
        return max;
    }
}

然后创建一个实例:

NoRepeatRandom nrr = new NoRepeatRandom(916132832);

要生成新代码,请使用:

int codeInt = nrr.next();
if (codeInt == -1)
{
    // All the codes are used, need to reset the random generator!
}
String code = toCode(codeInt);

唯一剩下的部分是设计toCode(int)方法:

public static final String charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw013456789";

public static String toCode(int i)
{
    String code = "";

    return code;
}

答案 4 :(得分:2)

我能想到的最佳解决方案是保持每日更新私钥。将密钥与移动电话号码结合使用以生成5位数代码并将此代码存储在数据库中。更新私钥时使代码无效并清除数据库。这样,您可以决定现有代码何时变为无效,而不是等待组合完成。这种方法使您可以灵活地将代码大小从5增加到任何其他大小,并且只存储已经使用过的值。

答案 5 :(得分:1)

是否真的有可能所有代码都用尽了?你说只有100万用户。如果我理解正确,这意味着您只需要生成100万个代码。如果是这种情况,那么可能的(例如5个字符)代码的数量远远大于您需要的数量,并且解决方案很简单:只需为新用户生成随机代码,直到找到一个没花。您可以在哈希表中存储和查找使用过的代码。

答案 6 :(得分:1)

如果您不需要强大的安全性,那么一个简单的方法就是

  1. x的{​​{1}}计数器0T-1保持一致T=62**n,其中n是您要使用的字符数。
  2. 对于每个代码,您只需增加x,然后使用(x * P) % T的编码,其中PT%的大量互质模数运算符。
  3. P互为T,保证映射(x * P) % T是双向投射,因此所有代码都将在第一个重复使用之前使用。

    因为有k所以k*P = 1 (mod T)因此y每个x = (k * y) % T的数字yx*P = (k*y) * P = y * (k*P) = y * 1 = y (mod T)相反,因为x -> (x * P) % T)所以变换T是满射的,因此也是单射的,因为这个空间是有限的。

    您也可以尝试使用一些更复杂的双射函数,例如将{{1}}限制为2的幂并使用位混洗,但是如果你真的需要安全性,那么最好只使用随机代码每次都可以检查它是否最近没有用过队列和位表或哈希表,取决于两者中的哪一个会更小。

答案 7 :(得分:1)

你有62个字符(A-Z,a-z,0-9)。 5字符代码实际上是5位数字的基数62。生成适当范围内的随机数并将其转换为base 62。

为避免重复,请采用足够大的数字范围并对范围进行随机播放。所有保证都是唯一的,而不是任何特定的顺序。

答案 8 :(得分:1)

听起来你需要一个线性同余发生器。 LCG是X(n + 1)=(a * X(n)+ c)mod m形式的简单递归函数。 LCG&lt; 124,3,916132832&gt;做你需要的,并击中周期中的每一个价值。循环中的每个值都将映射到您指定的5位数代码。

公平的警告,从你的描述中我假设你并不真正需要随机因素,只是保证每个值对于循环是唯一的。该算法并非一点安全。任何人都可以从你发出的最后一个代码进入循环。如果你确实需要一些随机性,那你就有点麻烦了。引用John von Neumann的话。 “任何考虑产生随机数字的算术方法的人当然都处于罪恶状态。”

答案 9 :(得分:0)

首先,为什么不使用UUID

但如果您想自己生成数字,请尝试以下方法:

预生成10-20百万个组合并将它们保存在内存中。 当你想要下一个id时,从它们中获取一个随机组合并从集合中删除它。

当设置为空时,使用原始组合重置设置(您可以保留原始设置的第二个副本以进行快速重置。)

答案 10 :(得分:-1)

如果您正在寻找非常简单的东西,请尝试以下方法:

Date date = new Date();
String random = String.valueOf(date.getTime()).substring(6);

这些数字在不久的将来永远不会重演!