信用卡费用子集的递归批量对帐

时间:2013-02-06 00:58:44

标签: php arrays recursion accounting

我找到了解决方案,请参阅下面的答案和/或Github Gist, which has newer optimizations

我有一系列的信用卡费用和一批总数...有时数量的SUM()=批量,因此很容易分组到批次,我们可以检查有关的交易当天存入支票账户。

有时,适当的金额用于这些费用的一部分,其中一个或多个在第二天进行批处理。 你能以编程方式解决这个问题吗?这是我的authorize.net批量会计,很糟糕,所以我正在为我的簿记员制作一个工具。

+------------+--------+
| transId    | amount |
+------------+--------+
| 2863996511 | 154.00 |
| 2863992361 | 762.20 |
| 2863898975 |  49.00 |
| 2863894564 |   5.44 |
| 2863879671 |  10.88 |
| 2863858891 | 209.00 |
| 2863856334 | 148.00 |
| 2863367273 | -25.01 |
+------------+--------+

当天的批次总额为1302.63美元。正如经常发生的那样,电荷没有在该批次中结束,因此批次是阵列中电荷总和的一部分。在这种情况下,10.88美元的费用是在第二天的批次。这一点伪代码可以通过两个嵌套的for循环来捕获它:

        for( $skipdx=0; $skipdx<$size; $skipdx++){
            $total=0;
            for( $idx=0; $idx<$size; $idx++){
                if($idx!=$skipdx){
                    $total+=$charges[$idx]['amount'];
                    $thisBatch[]=$charges[$idx]['transId'];
                }
                if( abs($total-$batch['total']) < .04 ) {
                    echo "sum of charges matches $date batch total: $$total  (line: ". __LINE__ .")\n";
                    $foundIt=TRUE;
                    break;
                }
            }
            if($foundIt==TRUE)
                break;
        }

如何动态选择搜索未添加TWO的费用?那么三个?我可以看到,如果$skipdx省略了一个电荷,那么跳过两个电荷会添加一个skip2dx嵌套循环。如果仍未找到,skip3dx将成为第3级嵌套。

我通常非常擅长算法直到递归然后我变得愚蠢。

2 个答案:

答案 0 :(得分:0)

它来找我!递归的“当前层”执行此操作:

  1. 总计收费金额。
  2. 通过一系列指控进行迭代, 一次减去1次充电并针对目标批次进行测试 量。
  3. 如果金额在此次迭代中匹配,则取消设置此费用的ID并返回 一系列结果。
  4. 如果找不到,请再次尝试收费 这次删除当前的费用。再次调用该函数 相同的目标数量和新缩短的数组相同 再次发挥作用。
  5. 工作代码(Github has newer optimizations也是):

    $charges=Array(
         '2863996511' => 154.00 ,'2863879671' => 10.88
        ,'2863992361' => 762.20 ,'2863858891' => 209.00
        ,'2863898975' => 49.00  ,'2863856334' => 148.00
        ,'2863894564' => 5.44   ,'2863367273' => -25.01
    ); print_r($charges);
    $targets=Array(1302.63, 1327.64, 1322.20 );
    foreach( $targets as $batch ) {
        printf( "We seek combination of transIds to = $%-7.2f\n", $batch);
        $answer=reco( $batch, $charges );
        if( is_array($answer) )
            echo eval( "return ".implode("+",$answer).";" )."\nWIN!\n";
        else
            echo "Bust!\n";
    }   
    
    function reco( $target, $arr ){
        if( count($arr) < 2 )
            return FALSE;
        $killid='';
        $sum = eval( "return ".implode("+",$arr).";" );
        foreach( $arr as $id=>$amt ){
            if( abs($sum-$target-$amt) < .01 ) {
                $killid=$id;
                break;
            }
        }
        if( strlen($killid) > 1 ) {
            echo ".  omit $killid : ".$arr[$killid]."\n";
            unset($arr[$killid]); 
            return $arr;
        }
        foreach( $arr as $id=>$amt ){
            $tmp=$arr;
            unset($tmp[$id]);
            $rtn = reco( $target, $tmp );
            if( is_array($rtn) ) {
                echo ".. omit $id : ".$arr[$id]."\n";
                return $rtn;
            }
        }
        return FALSE;
    }
    

    输出:

    We seek combination of transIds to = $1302.63
    . omit 2863879671 : 10.88
    1302.63
    WIN!
    We seek combination of transIds to = $1327.64
    . omit 2863367273 : -25.01
    .. omit 2863879671 : 10.88
    1327.64
    WIN!
    We seek combination of transIds to = $1322.20
    . omit 2863367273 : -25.01
    .. omit 2863879671 : 10.88
    .. omit 2863894564 : 5.44
    1322.2
    WIN!
    

    表现不是一个很大的问题;到大约一秒钟处理的第三级递归。

答案 1 :(得分:0)

当您开始查看收费组的所有可能子集时,您可能会注意到,有时候第一个与您的批次总和相匹配的子集并不是一个好的会计解决方案。可能有几种可能的费用子集累计到您的批次总数。

你会选择哪一个?

实际上这会导致一个众所周知的问题,称为子集和问题 - 请参阅wiki here

还有一个名为SumMatch的廉价Excel加载项,可以找到与给定总和匹配的所有数字组合。考虑是否值得您编程时间来处理正确的算法。