2 个不稳定版本
新 0.5.0 | 2024年8月14日 |
---|---|
0.4.2 | 2023年9月13日 |
146 在 科学 中排名
每月105 次下载
46KB
983 行
偿付
如果你有一个由彼此拥有债务的人组成的网络,偿付债务可能会导致许多交易。使用这个crate可以将交易量最小化。
工作原理
我们将网络表示为图。每个节点代表一个人,每个人需要支付/接收的金额由顶点权重表示。负权重表示对网络的净债务,而正权重表示网络对个人的债务。目标是找到表示现金流的定向加权边,使得对于每个人,其流入量减去其流出量等于其顶点权重(他们拥有/从网络中获得的金额)。此外,边的数量应尽可能少。
在 Crates 中的使用
生成图
图可以通过两种不同的方式生成。
通过加权有向边
在这种方法中,我们描述节点之间的边。每条边都有一个起始节点和一个结束节点,以及一个权重。每个顶点需要一个独特的名称。否则,两个顶点将被解释为相同的。我们展示了如何创建以下图。
graph TD;
A -- 1 --> C;
A -- 1 --> D;
B -- 1 --> D;
从这个表示生成的图将转换为 通过顶点权重 的图。
从 Vec<((String, String), i64)>
let input: Vec<((String, String), i64)> = vec![
(("A".to_string(), "C".to_string()), 1),
(("A".to_string(), "D".to_string()), 1),
(("B".to_string(), "D".to_string()), 1),
];
let graph: Graph = input.into();
这里的节点被命名为 A
,B
,C
,D
。
从 HashMap<(String, String), i64>
let mut input: HashMap<(String, String), i64> = HashMap::new();
input.insert(("A".to_string(), "C".to_string()), 1);
input.insert(("A".to_string(), "D".to_string()), 1);
input.insert(("B".to_string(), "D".to_string()), 1);
let graph: Graph = input.into();
这里的节点被命名为 A
,B
,C
,D
。
通过顶点权重
对于这种方法,我们将直接告诉图节点及其权重。这里有几种选择。权重表示一个人需要支付/接收的金额。我们展示了如何创建以下节点及其权重。
顶点 | A | B | C | D |
---|---|---|---|---|
权重 | -2 | -1 | 1 | 2 |
从 Vec<i64>
let input: Vec<i64> = vec![-2, -1, 1, 2];
let graph: Graph = input.into();
这里的节点名称只是从 0
到 n
的数字。因此,0
,1
,2
,3
。
从 Vec<(String, i64)>
let input: Vec<(String, i64)> = vec![
("A".to_string(), -2),
("B".to_string(), -1),
("C".to_string(), 1),
("D".to_string(), 2),
];
let graph: Graph = input.into();
这里的节点被命名为 A
,B
,C
,D
。
从 HashMap<String, i64>
let mut input: HashMap<String, i64> = HashMap::new();
input.insert("A".to_string(), -2);
input.insert("B".to_string(), -1);
input.insert("C".to_string(), 1);
input.insert("D".to_string(), 2);
let graph: Graph = input.into();
这里的节点被命名为 A
,B
,C
,D
。
解决方法
可用的求解器
求解器 | 类型 | 求解方法 | 描述 |
---|---|---|---|
星形扩展 | 2近似 | 星形扩展 | 通过选择中心节点来逼近最优解,该节点与所有边相连。 |
贪婪满足 | 2近似 | 贪婪满足 | 在最小化所有边的总权重的同时逼近最优解。 |
星形扩展分区 | 精确 | 分区星形扩展 | 基于精确求解器的分区,使用星形扩展解决基本案例。 |
贪婪满足分区 | 精确 | 分区贪婪满足 | 基于精确求解器的分区,使用贪婪满足解决基本案例。 |
星形扩展最佳分区 | 精确 | 分支 | 分支分区星形扩展 |
贪婪满足最佳分区 | 精确 | 分支分区贪婪满足 | 基于分支的精确求解器,运行时间为O*(3^n),使用贪婪满足解决基本案例。 |
近似算法不一定返回最优解,但它们的解不比给定因子更差。它们在多项式时间内运行。
精确算法给出最优解,但它的运行时间不是多项式的。这可能导致在处理较大输入时运行时间过长。通常情况下,没有实例,近似算法不会返回最优答案。
使用库
解决实例并获取字符串形式的解决方案。
use payback::graph::Graph;
use payback::probleminstance::{ProblemInstance, Solution, SolvingMethods};
let instance: ProblemInstance = Graph::from(vec![-2, -1, 1, 2]).into();
let solution: Solution = instance.solve_with(SolvingMethods::StarExpand);
let result: Result<String, String> = instance.solution_string(&solution);
解决实例并打印用于graphviz图形可视化的dot
字符串。
use payback::graph::Graph;
use payback::probleminstance::{ProblemInstance, Solution, SolvingMethods};
let instance: ProblemInstance = Graph::from(vec![-2, -1, 1, 2]).into();
let solution: Solution = instance.solve_with(SolvingMethods::StarExpand);
let result: Result<String, String> = instance.solution_to_dot_string(&solution);
您也可以选择除SolvingMethods::StarExpand
之外的求解方法。有关更多信息,请参阅解决方法。
作为CLI的使用
用法:payback [OPTIONS] <FILE> [OUTPUT] [METHOD]
有关[OPTIONS]
的详细信息,请参阅payback的帮助。 [OUTPUT]
指定结果应以何种格式返回到stdout。这里提供的是dot
和transactions
选项。使用dot
提供可由graphviz解析的输出,可以立即将其转换为图形。使用transactions
仅打印解决方案的边及其权重。 [METHOD]
确定求解算法,如#Solving中所述。 <FILE>
选项应给出图形。可以是文件或将其管道输入到stdin。格式是csv
。可以指定节点及其权重,如From Vec<(String, i64)>中所述,或者指定边及其权重,如From HashMap<(String, String), i64>中所述。
如果您要输入此图形
graph TD;
A -- 1 --> C;
A -- 1 --> D;
B -- 1 --> D;
可以使用csv输入问题。
A,-2
B,-1
C,1
D,2
A,C,1
A,D,1
B,D,1
示例
使用stdin与-
。默认值为[OUTPUT] = transactions
和[METHOD] = approx-star-expand
。
echo A,1\\nB,-1 | ./payback -
# "A" to "B": 1.0
这相当于
echo A,1\\nB,-1 | ./payback - transactions approx-star-expand
如果您已安装 graphviz,则可以使用结果创建PDF。
echo A,C,1\\nA,D,1\\nB,D,1 > test.csv
./payback test.csv dot partitioning-greedy-satisfaction | dot -Tpdf > out.pdf
这是生成的输出,在 out.pdf
。
graph TD;
A -- 2 --> D;
B -- 1 --> C;
注意
此问题为NP-Hard问题,因此对于较大的实例可能会有较长的运行时间。
依赖项
约10MB
约156K SLoC