根据公式计算参考编号

Calculating reference number from formula

提问人:Janne 提问时间:10/24/2023 最后编辑:mickmackusaJanne 更新时间:10/26/2023 访问量:103

问:

如何在有效的PHP代码中转换这个“参考编号”公式?

如果有人能帮助我,我会把 fromula 放在这里!

就我而言:

客户编号默认为 1 每次/always。

发票编号由用户确定

验证号的计算公式为:

参考号的验证位是通过从右到左乘以权重为 7、3、1、7、3、1...并总结这些产品。然后从下一个完整的十中减去乘积的总和,得到验证数字。如果结果为 10,则改用数字 0。

参考编号计算示例:客户编号:1

发票编号:25(发票编号至少有 4 位数字,如果插入的发票编号小于 4 位数字,如 25,则需要添加零 (0) 才能等于 4 位数字。所有零都将添加到左侧站点。在此示例中,25 -> 0025。如果客户编号是 4 位数字,例如 1234,则就是该数字)

参考编号以: 10025

5 -> 5 * 7 = 35

2 -> 2 * 3 = 6

0 -> 0 * 1 = 0

0 -> 0 * 7 = 0

1 -> 1 * 3 = 3

乘积之和 (35 + 6 + 0 + 0 + 3) 为 44。下一个完整的十是 50。因此,验证位数为 50 - 44 = 6。

最终参考编号为:100256

这是我尝试过的:

<?php
$customerNumber = 1;
$invoiceNumber = 765;

$baseReferenceNumber = "{$customerNumber} 0 {$invoiceNumber}";

$entries = str_split(str_replace(' ', '', $baseReferenceNumber));
$sum = 0;

foreach ($entries as $entry) {
    $sum += $entry * (($sum % 2) + 1);
}

$totalSum = $sum;
$nextFullTen = ceil($totalSum / 10) * 10;
$checkNumber = $nextFullTen - $totalSum;

$finalReferenceNumber = "{$baseReferenceNumber} {$checkNumber}";

echo "Final Reference Number: {$finalReferenceNumber}";
?>

但这给了我107653结果,而真正的答案是 1 07657

PHP 字符串 算法 数学

评论


答:

0赞 mickmackusa 10/24/2023 #1

声明常量值和用户输入变量/常量后,基本上有 5 个子任务。

  1. 使用填充零组合引用基本字符串(填充不是动态的)。
  2. 从最后一个到第一个迭代数字字符(您的迭代是从前面开始的)。
  3. 执行加权算术并对结果求和(请参阅我的模运算符数学)。
  4. 将总数向上舍入到最接近的 10,然后从该数字中减去总数以生成验证数字。
  5. 将该结果追加到参考库。

如果您喜欢经典的循环结构,循环将允许您从字符串的后面迭代字符。(演示for())

define('WEIGHTS', [7, 3, 1]);
define('WEIGHTS_COUNT', count(WEIGHTS));

$customerNumber = 1;
$invoiceNumber = 25;

$refBase = sprintf('%d%04d', $customerNumber, $invoiceNumber);

$weightedSum = 0;
for ($x = 0, $i = strlen($refBase) - 1; $i >=0; --$i, ++$x) {
    $weightedSum += $refBase[$i] * WEIGHTS[$x % WEIGHTS_COUNT];
}

echo $refBase . (ceil($weightedSum / 10) * 10 - $weightedSum);

如果您更喜欢函数式风格语法,则可以用作循环。声明只允许将计数器传递给后续迭代(否则它将被“遗忘”)。(演示array_reduce()static$i)

$refBase = sprintf('%d%04d', $customerNumber, $invoiceNumber);

$weightedSum = array_reduce(
                   array_reverse(str_split($refBase)),
                   function($result, $v) {
                       static $i;
                       $i ??= 0;
                       return $result + ($v * WEIGHTS[$i++ % WEIGHTS_COUNT]);
                   },   
                   0
               );

echo $refBase . (ceil($weightedSum / 10) * 10 - $weightedSum);

如果你希望有一个更丑陋的版本,就在这里。你可以通过内联声明将所有子任务分解成一个单行,然后连接一个 IIFE 的结果,这将消耗 重新调整的验证位。把你的barfbag放在手边。(演示$refBasearray_reduce())

echo ($refBase = sprintf('%d%04d', $customerNumber, $invoiceNumber))
     . $verificationDigit = (
         fn($weightedSum) => (ceil($weightedSum / 10) * 10 - $weightedSum)
       )(array_reduce(
             str_split(strrev($refBase)),
             function($result, $v) {
                 static $i;
                 $i ??= 0;
                 return $result + ($v * WEIGHTS[$i++ % WEIGHTS_COUNT]);
             },   
             0
          )
       );
0赞 Mr Gasto 10/24/2023 #2

这是解决方案之一,我试图让它变得非常简单易懂。

    $basis = [7, 3, 1, 7, 3, 1];
    $customer_number = 1;
    $invoice_number = 25;
    $padded_invoice_number = '';
    $padded_invoice_number = str_pad($invoice_number,4,"0", STR_PAD_LEFT);
    $reference_number_base = ( $customer_number . $padded_invoice_number);
    $reference_number_base_reversed = strrev($reference_number_base);
    $sum_of_the_products = 0;

    for ($i=0; $i < strlen($reference_number_base_reversed); $i++) { 
        $sum_of_the_products +=  (int) $reference_number_base_reversed[$i] * $basis[$i];
    }
    
    $next_full_ten = ceil($sum_of_the_products/10) * 10;
    $verification_digit = $next_full_ten - $sum_of_the_products;
    $final_reference_number  =  ($reference_number_base . $verification_digit);

评论

0赞 mickmackusa 10/24/2023
从长远来看,这个无法解释的答案可能不够可靠。随着客户编号和发票编号长度的增长,将需要更长的时间。这就是提问者和我实现基于模的数组访问作为算法的一部分的原因。$basis