簡體   English   中英

從PHP數組遞歸計算值的方法?

[英]Method for recursively calculating values from a PHP array?

這個問題在某種程度上與層次結構數據有關,但是我已經從數據庫中獲得了一個存儲過程,該存儲過程將返回下面提到的數據。 我沒有試圖找出如何構建層次結構(完成),而是試圖找到使用PHP(也許是遞歸數組)基於該層次結構計算數字的最佳方法。

通過使用自定義存儲過程,我可以拉出我們公司的銷售代表,他們銷售的產品的列表,以及這些產品在新行上及其后續銷售代表(如果在樹中)的“獲利”百分比。 。

返回的數據的示例如下:

Repnum | Name | productID | sales | earn | depth | parentRep
------------------------------------------------------------
1      |   A  |   1       | 5000  | 0.50 |  1    | 0
1      |   A  |   2       | 10000 | 0.35 |  1    | 0  
2      |   B  |   1       | 400   | 0.40 |  2    | 1
2      |   B  |   2       | 1000  | 0.30 |  2    | 1
7      |   E  |   1       | 30000 | 0.35 |  3    | 2
3      |   C  |   1       | 5000  | 0.33 |  2    | 1

可以安全地假設返回的數據的順序已經根據此處未提供的深度和信息進行了適當的格式化和排序。 在樹形視圖中,上面的數據如下所示:

1-A-1-5000-0.50-1-0
1-A-2-10000-0.35-1-0
   2-B-1-400-0.40-2-1
   2-B-2-1000-0.30-2-1
      7-E-1-30000-0.35-3-2
   3-C-1-5000-0.33-2-1

在我的while(result)循環中,我試圖為返回的每個repnum計算以下內容:

  • 該代表人數的銷售總額乘以每種產品的收入百分比(其總銷售額和收入金額)
  • 每個下線樹的總銷售額乘以當前代表及其下一層的差異

為了說明如何工作:

Rep 1收入:

Rep 1 total sales = 5000 + 10000 = 15,000
Rep 1 earn on self = (5000 * 0.50) + (10000 * 0.35) = 6,000 hello!

Rep 2 total sales = 400 + 1000 = 1400
Rep 1 earn on rep 2 sales = (400 * (0.50-0.40) + (1000 * (.35-0.30)) = 90

Rep 7 total sales = 30000
Rep 1 earn on rep 7 sales = (30000 * (0.50-0.40)) = 3000*
(* the reason the earn is calculated at rep 1 earn - rep 2 earn is because in any tree, the "parent" can only ever earn the difference of his/her earn and the direct depth below him/her)

Rep 3 total sales = 5000
Rep 1 earn on rep 3 sales = (5000 * (0.50-0.33)) = 850

**Rep 1 TOTAL earn = 6,000 + 90 + 3000 + 850 = 9,940**

代表2收入:

Rep 2 total sales = 400 + 1000 = 1400
Rep 2 total earn on self = (400 * 0.40) + (1000 * 0.30) = 460

Rep 7 total sales = 30000
Rep 2 total earn on rep 7 sales = (30000 * (0.40-0.35)) = 1500*
(* here, rep 2 earns the difference between him/her and rep 7 because he is in the depth right above it / his/her parent)

**Rep 2 TOTAL earn = 460 + 1500 = 1,960**

... 等等


我本可以構建腳本來使用HEAVY mysql遞歸,並為每個深度簡單地進行大量的while()循環,但發現它是不必要的,並且由於我使用存儲過程進行操作並預先計算層次結構而對系統造成負擔深度並適當地對數據進行排序。 計算最高級別的代表很簡單,但是要從列表中的第二個人返回(依此類推),然后重新開始,這是我在努力的地方。

我希望能夠根據上面的示例數據返回類似於以下內容的輸出:

num | Name | earnAmount
------------------------
1   |  A   | 9940
2   |  B   | 1960
7   |  E   | 10500
3   |  C   | 1650

提前謝謝您的幫助!

有關已問問題的注釋:

問:如何計算收入百分比?

答:它們不是計算出來的,而是由代表確定的特定ID,代表代表銷售。


問:您如何確定“級別”或父母/子女關系?

答:我不在這個例子中。 這部分方程式是通過MySQL存儲過程解決的,並且在許多方面都沒有關系(我也可以顯示每個rep的父repnum,但是由於數組是從上到下構建的,因此它可能不太有用)。 第一個表中示例數據的深度和排序順序已經過格式化和布局,可以通過PHP中的簡單print(results_array)語句打印每棵樹。


問:第一個表中productID的相關性是什么?

答:每個銷售代表都可以出售我們系統中可用productID分類的任何產品(數百種)。 每個銷售代表還可以賺取該特定產品每次銷售收入的一定百分比。 獲利百分比與特定產品類型(盡管每種產品都有最高和最低獲利)或特定深度(盡管深度較高的代表永遠不會比其下線樹的特定productID擁有較低的獲利百分比)完全無關。


問:如何在第一個表中對數據進行排序?

答:一點無關緊要(請相信我),但在幕后我創建了一個面包屑列,該列會使用當前的repnum,然后附加子項並基於該子項和此時樹的深度進行排序。 給出的示例:

0 (invisible parent which selects ALL sales reps)
  0-1
    0-1-2
      0-1-2-7
    0-1-3
...

這是我的方法。
我假設數組可以是一維的。 depth足以讓我們確定某個代表是否有“ 孩子 ”。 所以數組看起來像這樣:

$reps = array(
    array("rep" => "1", "name" => "A", "productId" => "1", "sales" => 5000, "earn" => 0.50, "depth" => "1"),
    array("rep" => "1", "name" => "A", "productId" => "2", "sales" => 10000, "earn" => 0.35, "depth" => "1"),
    array("rep" => "2", "name" => "B", "productId" => "1", "sales" => 400, "earn" => 0.40, "depth" => "2"),
    array("rep" => "2", "name" => "B", "productId" => "2", "sales" => 1000, "earn" => 0.30, "depth" => "2"),
    array("rep" => "7", "name" => "E", "productId" => "1", "sales" => 30000, "earn" => 0.35, "depth" => "3"),
    array("rep" => "3", "name" => "C", "productId" => "1", "sales" => 5000, "earn" => 0.33, "depth" => "2")
);

我決定采用遞歸方法。 我們在earn()函數中循環遍歷數組,每次迭代后都會前進數組指針。 在函數內部,我們在一個for循環中再次迭代,從current + 1數組元素開始直至結尾查找Rep的“ 子代 ”。 代碼如下所示:

function earn() {
    global $reps, $earns;
    $rep = current($reps);
    $key = key($reps);
    $immediateChildEarn = null;

    //basic Rep's earnings
    $earn = $rep['sales'] * $rep['earn'];

    //loop to find children with the same productId
    for ($i = $key + 1; $i < count($reps); $i++) {
        $child = $reps[$i];

        //we're only interested in Reps with the same product and deeper position
        if ($rep['productId'] !== $child['productId'] || $rep['depth'] >= $child['depth']) {
            continue;
        }

        //collect the earn of the immediate child
        if (null === $immediateChildEarn) {
            $immediateChildEarn = $child['earn'];
        }

        //the earn difference can't be greater than the difference between Rep and its first immediate child Rep
        if ($immediateChildEarn > $child['earn'] && $rep['depth'] + 1 < $child['depth']) {
            $child['earn'] = $immediateChildEarn;
        }

        //calculate the earnings gained from this child
        $earn += $child['sales'] * ($rep['earn'] - $child['earn']);
    }

    //just a quick fix to prevent throwing Notices - not significant for the algorithm itself
    if (!isset($earns[$rep['rep']])) {
        $earns[$rep['rep']] = 0;
    }

    $earns[$rep['rep']] += $earn;

    $finish = next($reps);
    if (false !== $finish) {
        earn();
    }
}

$earns = array();
reset($reps);
earn();

var_dump($earns)的結果將是:

Array
(
    [1] => 9940
    [2] => 1960
    [7] => 10500
    [3] => 1650
)

請隨意評論我的答案。 我將盡力修復所有錯誤並改進代碼。

復雜度
我不擅長計算算法的復雜度,但是在我的解決方案中,復雜度是我認為的:

  1. 時間復雜度
    • 最壞情況O(n logn)
    • 最佳情況O(2n)
  2. 內存復雜度(不包括輸入數組)
    • 最壞情況O(n)
    • 最佳情況O(1)

如果我錯了,請隨時糾正我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM