在PostgreSQL中选择最适合的事务组合

时间:2013-09-03 12:31:44

标签: php postgresql combinatorics bitcoin

我需要选择最佳比特币交易组合进行发送。我使用PHP实现了结果,但它使用了大量内存,数据库很有可能处理得更好。

整个交易清单:

+------------------+------+--------+
|  Transaction ID  | Vout | Amount |
+------------------+------+--------+
| transactionid1   |    0 | 10     |
| transactionid1   |    1 | 1.5    |
| transactionid2   |    0 | 0.5    |
| transactionid3   |    0 | 0.7    |
+------------------+------+--------+

我需要创建某种函数或选择查询,当我提供金额= 0.4时,它会返回我的行

+------------------+------+--------+
|  Transaction ID  | Vout | Amount |
+------------------+------+--------+
| transactionid2   |    0 | 0.5    |
+------------------+------+--------+

当我提供金额= 2.1

+------------------+------+--------+
|  Transaction ID  | Vout | Amount |
+------------------+------+--------+
| transactionid1   |    1 | 1.5    |
| transactionid3   |    0 | 0.7    |
+------------------+------+--------+

所以剩下的就是Knapsack problem了。这是我如何使用组合学解决我的问题。我已将交易数据压缩为$ key => $ value数组,其中$ key是transactionid_vout,value是amount。

$flatterTransactions = array(4) (
  [transactionid1_0] => (int) 10
  [transactionid1_1] => (float) 1.5
  [transactionid2_0] => (float) 0.5
  [transactionid3_0] => (float) 0.7
)

然后我从该交易中创建组合

$combinations = array(15) (
    [0] => Array
        (
            [transactionid1_0] => 10
        )

    [1] => Array
        (
            [transactionid1_1] => 1.5
        )

    [2] => Array
        (
            [transactionid2_0] => 0.5
        )

    [3] => Array
        (
            [transactionid3_0] => 0.7
        )

    [4] => Array
        (
            [transactionid1_0] => 10
            [transactionid1_1] => 1.5
        )

    [5] => Array
        (
            [transactionid1_0] => 10
            [transactionid2_0] => 0.5
        )

    [6] => Array
        (
            [transactionid1_0] => 10
            [transactionid3_0] => 0.7
        )

    [7] => Array
        (
            [transactionid1_1] => 1.5
            [transactionid2_0] => 0.5
        )

    [8] => Array
        (
            [transactionid1_1] => 1.5
            [transactionid3_0] => 0.7
        )

    [9] => Array
        (
            [transactionid2_0] => 0.5
            [transactionid3_0] => 0.7
        )

    [10] => Array
        (
            [transactionid1_0] => 10
            [transactionid1_1] => 1.5
            [transactionid2_0] => 0.5
        )

    [11] => Array
        (
            [transactionid1_0] => 10
            [transactionid1_1] => 1.5
            [transactionid3_0] => 0.7
        )

    [12] => Array
        (
            [transactionid1_0] => 10
            [transactionid2_0] => 0.5
            [transactionid3_0] => 0.7
        )

    [13] => Array
        (
            [transactionid1_1] => 1.5
            [transactionid2_0] => 0.5
            [transactionid3_0] => 0.7
        )

    [14] => Array
        (
            [transactionid1_0] => 10
            [transactionid1_1] => 1.5
            [transactionid2_0] => 0.5
            [transactionid3_0] => 0.7
        )

)

然后我通过组合并用得分创建总和组合数组。这里的评分目标是使用更少的交易。

$summedCombinations[$key] = array(
                'sum' => $sum,
                'count' => count($combination),
                'score' => $sum * (count($combination) * 2)
            );

毕竟我按照字段过滤数组,只留下覆盖我金额的交易。按分数排序并获得最佳匹配。

1 个答案:

答案 0 :(得分:0)

最好在这里有点问题,但这就是我如何去做。无论你如何做,你都必须处理一个选择组合的算法。

 WITH target AS (select ? as target),
 RECURSIVE largest_txs AS (
      select transaction_id, vout, amount, amount as running_total
        from transactions
  CROSS JOIN target 
       where amount < target.target
    order by amount desc limit 1
   UNION ALL
      SELECT t.transaction_id, t.vout, t.amount, t.amount + l.running_total
        FROM transactions
        JOIN (select * from largest_txs order by amount asc limit 1) l
  CROSS JOIN target 
       WHERE amount < target.target + l.running_total AND t.transaction_id NOT IN
             (select transaction_id from largest_txs)
 )
 SELECT transaction_id, vout, amount, running_total FROM largest_txs
  UNION
 SELECT t.transaction_id, t.vout, t.amount, null
   FROM transactions t 
  WHERE t.transaction_id NOT IN (select transaction_id from largest_txs)
  ORDER BY amount asc limit 1;

这应该是可以预期的。如果表格具有任何大小,你肯定会想要一个金额索引。