【问题标题】:is there a difference in the result from these two algorithms?这两种算法的结果有区别吗?
【发布时间】:2010-07-14 07:45:42
【问题描述】:

这两种算法用于检查有效的会员号 第一个是公司给我的, 第二个是我设计的,从我的测试中我看不出它们在功能上的任何区别,

在任何情况下任何人都可以看到他们会在哪里返回不同的输出?

测试输入: 6014355021355010 或者 6014355065446212 或者 6014351000254605

校验位使用前15位计算如下:

  1. 从左到右将偶数位的数字相加
  2. 将奇数位置的每个数字相乘(从左到 对)按数字 2. 如果有任何结果 是 2 位数字,将数字相加为 1。 将每个数字相加 乘以最终结果。
  3. 添加第 1 步和第 2 步的最终结果。
  4. 从第 3 步中取出结果的最后一位,然后从 10 中减去 给出校验位。
  5. 从 16 位数字中取出最后一位数字并与校验位进行比较
  6. 如果相等,则有效

校验位使用整16位计算如下:

  1. 从左到右将偶数位的数字相加
  2. 将奇数位置的每个数字相乘(从左到 对)按数字 2. 如果有任何结果 是 2 位数字,将数字相加为 1。 将每个数字相加 乘以最终结果。
  3. 添加第 1 步和第 2 步的最终结果。
  4. 取最终结果和模数 10
  5. 如果结果为0,则有效


更新:
好吧。我试图在 php 中创建这两种算法, 第二个,我已经创建成功了, 然而,第一个,我似乎无法开始工作。

可能我读错了,但是,这是我为第一个算法给出的原始简介:

16位数模10校验位计算

校验位使用前15位计算如下:
1.从左到右偶数位的数字相加

2 . 将奇数位置(从左到右)的每个数字乘以数字 2
如果任何结果为 2 位,则将这些数字相加为 1。
将每次乘法的数字相加成最终结果。

3. 将第 1 步和第 2 步的最终结果相加。

4. 从第 3 步中取出结果的最后一位,然后从 10 中减去得到校验位。
如果步骤 3 的结果是 10 的倍数,则校验位为零。


示例 6014 3590 0000 0928
1.0 0 + 4 + 5 + 0 + 0 + 0 + 9 = 18
2.0 6 * 2 = 12 所以 1 + 2 = 3
2.1 1 * 2 = 2
2.2 3 * 2 = 62.3 9 * 2 = 18 所以 1 + 8 = 9
2.4 0 * 2 = 0
2.5 0 * 2 = 0
2.6 0 * 2 = 0
2.7 2 * 2 = 4
2.8 3 + 2 + 6 + 9 + 0 + 0 + 0 + 4 = 24
3.0 18 + 24 = 42
4.0 校验位是 10 - 2 = 8
5.0 8 = 第 16 位 (601435900000092[8])


更新 2:
好的,所以我已经更正了算法,

另外,我应该提一下,还有另外两项检查 如果(数字的长度!= 16) 返回 1; 和 如果(前 5 个字符!= 601435) 返回 1;

那么有什么对策吗?

干杯, 马特


算法测试 [php]
<?php
$file = file_get_contents('fb.csv');
$numbers = explode("\n", $file);

function validate_flybuys($number) {
    $r = array ('o' => '0', 'i' => '1', 'l' => '1', 'e' => '3', ' ' => '');
    $flybuys = trim(strtolower($number));
    $flybuys = str_replace(array_keys($r), $r, $flybuys);
    if('601435' != substr($flybuys, 0, 6) || strlen($flybuys) != 16)
            return 1;
    $evens = 0;
    $odds = '';

    for($i = 0; $i <= 15; $i+=2) {
        $odds .= $flybuys[$i];
        $evens += $flybuys[$i+1];
    }

    $odds = str_split($odds);
    foreach($odds as &$odd) {
        $odd = $odd*2;
        if($odd >= 10) {
            $odd = str_split($odd);
            $odd = $odd[0] + $odd[1];
        }
    }
    return (array_sum($odds)+$evens) % 10;
}

function validate_flybuys2($number) {
    $r = array ('o' => '0', 'i' => '1', 'l' => '1', 'e' => '3', ' ' => '');
    $flybuys = trim(strtolower($number));
    $flybuys = str_replace(array_keys($r), $r, $flybuys);
    if('601435' != substr($flybuys, 0, 6) || strlen($flybuys) != 16)
            return 1;
    $evens = 0;
    $odds = '';

    for($i = 0; $i <= 14; $i+=2) {
        $odds .= $flybuys[$i];
        if($i != 14)
            $evens += $flybuys[$i+1];
    }

    $odds = str_split($odds);
    foreach($odds as &$odd) {
        $odd = $odd*2;
        if($odd >= 10) {
            $odd = str_split($odd);
            $odd = $odd[0] + $odd[1];
        }
    }
    $total = (array_sum($odds))+$evens;
    $total = str_split($total);
    $check = 10 - $total[1];
    $check = $check % 10;
    if($check == substr($flybuys, 15, 1))
        return 0;
    else
        return $check;
}

foreach($numbers as $number) {
    $valid = validate_flybuys($number);
    $valid2 = validate_flybuys2($number);
    if($valid != $valid2 || $valid != 0) {
        echo '<hr />';
        echo 'NUMBER: '.$number.'<br />';
        echo 'V1: '.$valid.'<br />';
        echo 'V2: '.$valid2.'<br />';
    }
}

如果有人有兴趣和 cmets,我可以发布一些样本编号进行测试:)
哦,随意优化代码 8D

【问题讨论】:

  • 从您添加的第一个算法的第 5 步的简介中,不应该是模数。它应该是计算的校验位和第 16 位之间的相等校验(或者如果结果为 0,则进行减法运算,然后进行校验)。随着这种变化,两种算法的思想都相同。
  • (题外话:)我觉得很有趣,你会花这么多时间在贵公司给你的(工作)算法上......这是过早优化的例子吗? :-)
  • 不确定您对原始算法的描述中的第 5 步来自哪里...我认为他们宁愿检查它是否最后一个数字,但只有你老板可以告诉你...
  • @Hailwood:我完全是出于好奇,这是一个很好的谜题。然而,在决定在实践中哪个更好时,我会选择原来的那个,主要是因为如果算法被证明是错误的,没有人可以责怪你。如果你最终使用你的算法,你必须准备好为它辩护(例如,有一个算法是等效的声音证明)。但这可能还不够:根据我的经验,一些被激怒的老板甚至不会接受可靠的论据,他们只会看到你无缘无故地改变了一些(假设是)正确的。
  • 您所追求的似乎是一个基本的信用卡验证算法。 en.wikipedia.org/wiki/Luhn_algorithm

标签: algorithm


【解决方案1】:

编辑:此证明仅在第一个算法的第 5 步和第 6 步是相等检查而不是模数计算时才有效。 cmets 中提到的原始摘要似乎是平等检查的意思。

EDIT2:我认为第一个算法应该是这样的。但您最好验证这一点,也许来自给您原始简报的人。

  1. 从左到右偶数位的数字相加
  2. 将奇数位置(从左到右)的每个数字乘以数字 2。如果任何结果为 2 位,则将这些数字相加为 1。将每次乘法的数字相加成最终结果。
  3. 添加第 1 步和第 2 步的最终结果。
  4. 将第 3 步的结果的最后一位数字减去 10 得到校验位。
  5. 取 16 位数字的最后一位,如果与计算的校验位相同,则该数字有效

要在数学上验证两种算法是否相等,您可以使用congruency

假设a 是第一个算法步骤 3 的总和,b 是第二个算法步骤 3 的总和,c 是第 16 位(校验位)。

ab 的区别在于c 被添加到b 而不是a,这意味着:

a ≡ b - c mod 10

第一个算法的检查是通过从 10 中减去 a 来执行的,并检查它是否与模数 10 一致 c。(对于加法和减法,执行模数时无关紧要)

10 - a ≡ c mod 10

这等于:

-a ≡ c mod 10

现在你可以用第一个替换a,结果是

-(b - c) ≡ c mod 10

这等于:

c - b ≡ c mod 10

这等于:

-b ≡ 0 mod 10
b ≡ 0 mod 10

这就是第二个算法中执行的检查。所以两种算法都返回相同的结果。

【讨论】:

  • 那你肯定能证明我的反例 0000000000000257 错了吗?!不确定您在这里证明了什么,但显然不是算法。
  • 从更新中添加的原始简报中,我读到计算的校验位必须与第 16 位相等。这也是生成第 16 个类似 crc 的数字以证明正确性的意义所在。因此,对于您的示例,总和为 2+1=3,校验位为 10-3=7,等于第 16 位。
  • 是的,相等检查更有意义。现在,这是令人耳目一新的数学倒叙,所以+1。 :-)
【解决方案2】:

Edit2:请参阅我的其他答案,其中包含正确算法的反例。

编辑:我在第二种算法中使用的是 15 而不是 16 个数字。

它们不等价。

取 383838383838383-6 对第一个算法有效,但第二个算法给出 4 作为校验位!= 0。

编辑:偶数部分的总和为 56,奇数部分的总和为 48,总和为 104。

【讨论】:

  • 对于这个例子,两个算法都说这个数字是有效的。对于第二个算法,你取整个 16 位数字!所以偶数部分的总和是 62,不均匀部分的总和是 48,即 110 和 110%10==0。
  • 您的考试号码不是 16 位数字。你怎么能比较没有完整数字的算法?更重要的是,第 2 步的结果(计算奇数的总和)应该是 45,而不是 48:2*3 + 2*3 + 2*3 + 2*3 + 2*3 + 2*3 + 2*3 + 3 = 45(其中最后 3 个结果来自 2*6 = 12,其数字相加为 3)。
  • @stakx 我认为他的意思是 3838 3838 3838 383 - 6
  • 对,错过了校验位前的最后 3 个。但无论如何它都是错误的:-)
  • 上述 rudi-moore 建议的测试编号适用于两种算法。
【解决方案3】:

算法是不同的

0000000000000257

原始算法说它无效:偶数和是 2,赔率和是 1 => 总和 3. 10-3 = 7. 257 MOD 7 = 5 != 0 => 无效

您的算法总和为 9,赔率为 1 => 总计 10。10 MOD 10 == 0 => 有效。

所以它们不等价

qed。 :-)

【讨论】:

  • 应该是7 mod 7 = 0,有效。但我同意你的观点,第一个发布的算法有反例。 0600 0000 0000 000 - 8 例如。第一种算法的总和是 6。10-6=4。 8 mod 4 = 0 -> 有效。第二种算法的总和 14. 4 != 0 -> 无效。这就是我添加评论的原因。因为我认为第一个算法的模数应该是一个相等的检查(最初的简短说太想了)。例如: 8 != 4 -> 无效。使用相等的检查而不是模数,两者都相等,我在回答中证明了这一点。
【解决方案4】:

您的 php 代码有一些问题。

$check = 10 - $total[1]; 仅在总和为 2 位数字时有效。因为您的号码总是以601435 开头,所以总和不少于2 位。但至少 60143599999999906014359999999999 在 V2 中会被验证错误。

return $check; 行可以返回 0。这样60143550213550126014355021355017 被验证为有效,而它们不是。

我会替换这些行:

$total = str_split($total);
$check = 10 - $total[1];
$check = $check % 10;
if($check == substr($flybuys, 15, 1))
    return 0;
else
    return $check;

return (substr($flybuys, 15, 1) + $total) % 10;

所以V1V2 返回相同的值。

【讨论】:

    猜你喜欢
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-08
    • 2019-03-31
    • 2020-01-22
    相关资源
    最近更新 更多