我有2个列表具有相同的对象,有3个属性(accNo,accType& balance)。
List<> CSList
'CS1', 'CS', 3000
'CS2', 'CS', 2000
'CS3', 'CS', 1000
List<> CLList
'CL1', 'CL', 4000
'CL2', 'CL', 500
'CL3', 'CL', 1000
在我的示例中,所有acctype = CS(3000+2000+1000=6000)
的余额总和将始终大于或等于acctype = CL(4000+500+1000=5500)
。
我正在尝试按CL
余额清算CS
余额。例如,余额为4000的帐户CL1
可由CS1 & CS3(3000+1000)
或CS1 & CS2(3000+1000(CS2 remaining balance 1000 can be used next))
清算。
我想找出最少的交易数量,使CL
个帐户余额为CS
个帐户(首选输出2)。
Possible output 1:
Debit Credit Amount
CS1 CL1 3000
CS2 CL1 1000
CS2 CL2 500
CS2 CL3 500
CS3 CL3 500
Possible output 2:
Debit Credit Amount
CS1 CL1 3000
CS3 CL1 1000
CS2 CL2 500
CS2 CL3 1000
答案 0 :(得分:2)
贪婪的方法不会产生这个问题的最佳结果。 找到最佳交易次数的解决方案:
如果您拥有M个CS账户和N个账户,那么复杂性可能是O(M N * 2 N 2 )
答案 1 :(得分:1)
获得最佳结果的一种方法是首先将信用与相同的借方匹配 然后匹配其余部分并在必要时减去直至完全消耗。
根据借方和贷方的排序方式,您会得到不同的结果 但是当两者都按升序值排序时,它似乎工作得很好。
下面是一个用于演示该策略的javascript示例代码:
var debits = [ [ 'CS1', 3000 ], [ 'CS2', 2000 ], [ 'CS3', 1000 ], ['CS4',800] ];
var credits = [ [ 'CL1', 4000 ], [ 'CL2', 500 ], [ 'CL3', 1000 ], ['CL4',800] ];
console.log("\n# Debits and Credits sorted by ascending value\n\n");
sort_arrays(debits, 1, 1); //sort debits by ascending value
sort_arrays(credits, 1, 1); //sort credits by ascending value
console.log("Debits : "+JSON.stringify(debits));
console.log("Credits: "+JSON.stringify(credits));
var result = match_debits_to_credits (debits, credits);
sort_arrays(result, 1, 1);
console.log("Result : "+JSON.stringify(result));
console.log("\n# Debits and Credits sorted by descending value\n\n");
sort_arrays(debits, 1, -1); //sort debits by descending value
sort_arrays(credits, 1, -1); //sort credits by descending value
console.log("Debits : "+JSON.stringify(debits));
console.log("Credits: "+JSON.stringify(credits));
var result = match_debits_to_credits (debits, credits);
sort_arrays(result, 1, 1);
console.log("Result : "+JSON.stringify(result));
console.log("\n# Credits and Debits sorted by ascending name.\n# And without matching the same values first.\n\n");
sort_arrays(debits, 0, 1); //sort debits by ascending name
sort_arrays(credits, 0, 1); //sort credits by ascending name
console.log("Debits : "+JSON.stringify(debits));
console.log("Credits: "+JSON.stringify(credits));
var result = match_debits_to_credits (debits, credits, false);
sort_arrays(result, 1, 1);
console.log("Result : "+JSON.stringify(result));
function sort_arrays (p_array, index = 0, order_direction = 1){
p_array.sort(
function(a, b){
return a[index] === b[index] ? 0 : a[index]<b[index] ? -order_direction : order_direction
});
}
function match_debits_to_credits (p_debits, p_credits, p_match_equal_first = true){
let output = [];
let v_debits = JSON.parse(JSON.stringify(p_debits));
let v_credits = JSON.parse(JSON.stringify(p_credits));
if(p_match_equal_first){
// First match the credit with an equal debit
for (let i=0; i < v_credits.length; i++){
for (let j=0; j < v_debits.length; j++){
if (v_credits[i][1] == v_debits[j][1]){
console.log('credit ' + v_credits[i] + ' = debit ' + v_debits[j]);
output.push([ v_debits[j][0], v_credits[i][0], v_credits[i][1] ]);
v_credits.splice(i,1); i--; //consumed by the matching debit
v_debits.splice(j,1); //consumed by the matching credit
break;
}
}
}
}
for (let i=0; i < v_credits.length; i++){
for (let j=0; j < v_debits.length && v_credits[i][1] > 0; j++){
if (v_debits[j][1] > 0 && v_credits[i][1] > v_debits[j][1]){
console.log('credit ' + v_credits[i] + ' > debit ' + v_debits[j]);
v_credits[i][1] = v_credits[i][1] - v_debits[j][1];
output.push([ v_debits[j][0], v_credits[i][0], v_debits[j][1] ]);
v_debits.splice(j,1); j--; //consumed by the credit
}
else if (v_credits[i][1] <= v_debits[j][1]){
console.log('credit ' + v_credits[i] + ' <= debit ' + v_debits[j]);
v_debits[j][1] = v_debits[j][1] - v_credits[i][1];
output.push([ v_debits[j][0], v_credits[i][0], v_credits[i][1] ]);
if(v_debits[j][1] == 0) {v_debits.splice(j,1);}
v_credits.splice(i,1); i--; //consumed by the debit
break; //no need for further matching for this credit
}
}
}
return output;
}
&#13;
虽然不知道如何计算复杂性。
由于在信用完全消耗时退出循环的中断
如果M是信用数,N是债务数
然后我假设复杂性可能类似于O(M*N+M*2)
?
答案 2 :(得分:0)
想想我会尝试在SQL中执行此操作 在我的另一个答案中使用相同的策略。
下面的T-SQL适用于SQL Server。
declare @Transactions table (id int identity(1,1) primary key, name varchar(30), type char(2), value int, accountid int);
insert into @Transactions (name, type, value, accountid) values
('CS1','CS',3000,1),('CS2','CS',2000,1),('CS3','CS',1000,1),
('CL1','CL',4000,1),('CL2','CL', 500,1),('CL3','CL',1000,1);
declare @Credits table (creditid int identity(1,1) primary key, name varchar(30), value int);
declare @Debits table (debitid int identity(1,1) primary key, name varchar(30), value int);
declare @Result table (resultid int identity(1,1) primary key, accountid int,
credit_name varchar(30), credit_value int, debit_name varchar(30), debit_value int, debited int, credit_remaining int,
loopcnt int, comparison varchar(2));
declare @AccountId int;
set @AccountId = 1;
insert into @Credits (name, value)
select name, value
from @Transactions
where type = 'CL' and accountid = @AccountId
order by value asc, id;
insert into @Debits (name, value)
select name, value
from @Transactions
where type = 'CS' and accountid = @AccountId
order by value asc, id;
declare @TotalCredits INT;
declare @TotalDebits INT;
declare @CounterCredits INT;
declare @CounterDebits INT;
declare @CurrentCreditName varchar(30);
declare @CurrentCreditValue int;
declare @RemainingCreditValue int;
declare @Debited int;
declare @LoopCnt int = 0;
declare @CurrentDebitName varchar(30);
declare @CurrentDebitValue int;
set @TotalCredits = (select count(*) from @Credits);
set @TotalDebits = (select count(*) from @Debits);
set @CounterCredits = 1;
WHILE @CounterCredits <= @TotalCredits
BEGIN
select @CurrentCreditName = name, @CurrentCreditValue = value, @RemainingCreditValue = value
from @Credits where creditid = @CounterCredits;
set @CounterDebits = 1;
WHILE @RemainingCreditValue > 0 AND @CounterDebits <= @TotalDebits
BEGIN
set @LoopCnt = @LoopCnt + 1;
select @CurrentDebitName = name, @CurrentDebitValue = value
from @Debits where debitid = @CounterDebits;
if(@RemainingCreditValue = @CurrentDebitValue)
begin
set @Debited = @CurrentDebitValue;
set @RemainingCreditValue = 0;
update @Credits set value = 0 where creditid = @CounterCredits;
update @Debits set value = 0 where debitid = @CounterDebits;
insert into @Result (accountid, credit_name, credit_value, debit_name, debit_value, debited, credit_remaining, loopcnt, comparison)
values (@AccountId, @CurrentCreditName, @CurrentCreditValue, @CurrentDebitName, @CurrentDebitValue, @Debited, @RemainingCreditValue, @LoopCnt, '=');
end;
set @CounterDebits = @CounterDebits + 1;
END;
set @CounterCredits = @CounterCredits + 1;
END;
set @CounterCredits = 1;
WHILE @CounterCredits <= @TotalCredits
BEGIN
select @CurrentCreditName = name, @CurrentCreditValue = value, @RemainingCreditValue = value
from @Credits where creditid = @CounterCredits;
set @CounterDebits = 1;
set @TotalDebits = (select count(*) from @Debits);
WHILE @CounterDebits <= @TotalDebits AND @RemainingCreditValue > 0
BEGIN
set @LoopCnt = @LoopCnt + 1;
select @CurrentDebitName = name, @CurrentDebitValue = value
from @Debits where debitid = @CounterDebits;
IF(@CurrentDebitValue > 0 AND @RemainingCreditValue >= @CurrentDebitValue)
BEGIN
set @Debited = @CurrentDebitValue;
set @RemainingCreditValue = @RemainingCreditValue - @CurrentDebitValue;
update @Credits set value = @RemainingCreditValue where creditid = @CounterCredits;
update @Debits set value = 0 where debitid = @CounterDebits;
insert into @Result (accountid, credit_name, credit_value, debit_name, debit_value, debited, credit_remaining, loopcnt, comparison)
values (@AccountId, @CurrentCreditName, @CurrentCreditValue, @CurrentDebitName, @CurrentDebitValue, @Debited, @RemainingCreditValue, @LoopCnt, '>=');
set @CurrentCreditValue = @RemainingCreditValue;
END;
ELSE IF(@RemainingCreditValue > 0 AND @RemainingCreditValue < @CurrentDebitValue)
BEGIN
set @Debited = @RemainingCreditValue;
set @RemainingCreditValue = 0;
update @Credits set value = 0 where creditid = @CounterCredits;
update @Debits set value = value - @Debited where debitid = @CounterDebits;
insert into @Result (accountid, credit_name, credit_value, debit_name, debit_value, debited, credit_remaining, loopcnt, comparison)
values (@AccountId, @CurrentCreditName, @CurrentCreditValue, @CurrentDebitName, @CurrentDebitValue, @Debited, @RemainingCreditValue, @LoopCnt, '<');
END;
set @CounterDebits = @CounterDebits + 1;
END;
set @CounterCredits = @CounterCredits + 1;
END;
select
debit_name as Debit,
credit_name as Credit,
debited as Amount
from @Result
order by debit_name, credit_name;
<强>结果:强>
Debit Credit Amount
----- ------ ------
CS1 CL1 2500
CS2 CL1 1500
CS2 CL2 500
CS3 CL3 1000