优惠券代码生成

psunt 发布于 2019-01-24 algorithm 最后更新 2019-01-24 21:14 64 浏览

我想生成优惠券代码,例如AYB4ZZ2。不过,我还希望能够标记已使用的优惠券并限制其全球编号,比如N。天真的做法就像“生成N独特的字母数字代码,将它们放入数据库并对每个优惠券操作执行数据库搜索。” 但是,据我所知,我们也可以尝试找到一个函数MakeCoupon(n),它将给定的数字转换成具有预定义长度的类似优惠券的字符串。 据我所知,MakeCoupon应该满足以下要求:

  • 是双面的。这是反MakeNumber(coupon)应该可以有效计算。
  • MakeCoupon(n)的输出应该是字母数字的,并且应该具有小的和恒定的长度 - 以便它可以被称为人类可读的。例如。 SHA1摘要不会通过此要求。
  • 实用的独特性。对于每个自然n <= NMakeCoupon(n)的结果应该是完全唯一的或唯一的,例如,MD5是唯一的(具有相同的非常小的碰撞概率)。
  • (这个很难定义)如何从一个优惠券代码中枚举所有剩余的优惠券并不明显 - 比方说,MakeCoupon(n)MakeCoupon(n + 1)应该在视觉上有所不同。
    E.g. MakeCoupon(n), which simply outputs n padded with zeroes would fail this requirement, because 000001 and 000002 don't actually differ "visually".

问: 是否存在满足以下要求的任何函数或函数发生器?我的搜索尝试仅将我引向[CPAN] CouponCode,,但它并未满足相应函数为双射的要求。

已邀请:

vsit

赞同来自:

你想要的是Format-preserving encryption。 不失一般性,通过在基数36中编码,我们可以假设我们在0..M-1而不是符号串中讨论整数。 M应该是2的幂。 选择密钥并指定M后,FPE会为您提供0..M-1 encrypt的伪随机排列及其反decrypt

string GenerateCoupon(int n) {
    Debug.Assert(0 <= n && n < N);
    return Base36.Encode(encrypt(n));
}
boolean IsCoupon(string code) {
    return decrypt(Base36.Decode(code)) < N;
}
如果你的FPE是安全的,这个方案是安全的:即使他设法猜出与他知道的每个优惠券相关的数字,攻击者也无法生成其他优惠券代码,其概率高于O(N / M)。 。 这仍然是一个相对较新的领域,因此很少有这种加密方案的实现。 This crypto.SE question仅提到Botan,这是一个带有Perl / Python绑定的C++库,但不是C#。 注意事项:除了还没有公认的FPE标准这一事实外,您必须考虑实施中存在错误的可能性。如果线上有很多钱,你需要权衡这个风险与避免数据库相对较小的好处。

tvelit

赞同来自:

虽然我可能会接受这个答案,但我觉得我需要回应 - 我真的希望你能听到我说的话,因为它来自很多痛苦的经历。 虽然这项任务在学术上具有挑战性,而且软件工程师倾向于挑战他们的互动与解决问题,但如果可能的话,我需要为此提供一些指导。世界上没有零售商店,无论如何都有任何成功,它不能很好地跟踪所生成的每个实体;从每件库存到他们寄出这些门的每张优惠券或礼品卡。如果你是这样的话,那就不是一个好的管家了,因为如果人们要欺骗你,那就不是时候了,所以如果你在你的武器库里有你所有可能的项目,你就会做好准备。 现在,我们来谈谈在您的方案中使用优惠券的过程。 当客户兑换优惠券时,前面会有某种POS系统吗?这甚至可能是一个在线业务,然后他们可以只输入他们的优惠券代码与收银员正确扫描条形码的寄存器(我假设这是我们在这里处理的)?所以现在,作为供应商,你说如果你有一个有效的优惠券代码,我会给你一些折扣,因为我们的目标是生成可逆的优惠券代码,我们不需要数据库为了验证代码,我们可以正确地反转它!我的意思是这只是数学吗?嗯,是的,不。 是的,你是对的,这只是数学。事实上,这也是问题,因为cracking SSL也是如此。但是,我将假设我们都意识到SSL中使用的数学比这里使用的任何东西都要复杂得多,而且密钥要大得多。 这对你来说并不是理所当然的,你也不应该尝试提出某种方案,而你肯定没有人愿意打破这种方案,特别是涉及金钱时。你正在努力解决一个你不应该试图解决的问题,因为你需要保护自己免受使用优惠券代码的人的影响。 因此,这个问题不必要地复杂化并且可以像这样解决。

// insert a record into the database for the coupon
// thus generating an auto-incrementing key
var id = [some code to insert into database and get back the key]
// base64 encode the resulting key value
var couponCode = Convert.ToBase64String(id);
// truncate the coupon code if you like
// update the database with the coupon code
  1. 创建一张具有自动递增键的优惠券表。
  2. 插入该表并返回自动递增键。
  3. Base64将该ID编码为优惠券代码。
  4. 如果需要,可截断该字符串。
  5. 使用刚刚插入的优惠券将该字符串存回数据库。

jfugit

赞同来自:

内容太长未翻译

et_et

赞同来自:

您可以使用base-36号码系统。假设您想要coupen输出中的6个字符。 MakeCoupon的伪代码 MakeCoupon(N) { 有一个固定大小的字节数组,比如说6.将所有值初始化为0。 将数字转换为base - 36并将'digits'存储在数组中 (使用整数除法和mod运算) 现在,对于每个'数字'找到相应的ascii代码假设 数字从0..9开始,A..Z 使用此对流输出六位数作为字符串。 } 现在计算返回的数字与此操作相反。 这适用于具有6个允许字符的非常大的数字(35 ^ 6)。

oqui

赞同来自:

  • 选择加密函数c。对c有一些要求,但现在我们来看看SHA1。
  • 选择一个密钥k
您的优惠券代码生成功能可以是,对于号码n
  • 将n和k连接为"n"+"k"(这在密码管理中称为salting)
  • 计算c(“n”+“k”)
  • SHA1的结果为160位,将它们(例如使用base64)编码为ASCII字符串
  • 如果结果太长(正如您所说的那样是SHA1),请截断它以仅保留前10个字母并将此字符串命名为s
  • 您的优惠券代码为printf "%09d%s" n s,即零填充的n和截断的哈希s的串联。
是的,猜测n优惠券代码的数量是微不足道的(但见下文)。但是很难生成另一个有效的代码。 您的要求得到满足:
  1. 要计算反向功能,只需阅读代码的前9位数
  2. 即可
  3. 长度始终为19(n的9位数,加上10个哈希字母)
  4. 这是唯一的,因为前9位是唯一的。最后10个字符也很有可能。
  5. 即使猜到你使用了SHA1,也不清楚如何生成哈希。
点击 一些评论:
  • 如果你担心阅读n太明显了,你可以轻易地对它进行模糊处理,比如base64编码,并在代码中交替使用ns的字符。
  • 我假设您不需要超过十亿个代码,因此在9位数上打印n,但您当然可以将参数9和10调整为所需的优惠券代码长度。
  • SHA1只是一个选项,您可以使用其他加密函数,如私钥加密,但是在截断和提供明文时,您需要检查此函数是否保持强大。
  • 这在代码长度上不是最佳的,但具有简单和广泛可用的库的优点。