累积算法结构

时间:2016-01-07 12:31:18

标签: c# algorithm pseudocode

我有一张桌子,可以找到与帐户相关的交易。 该表包含借方和贷方交易。

我需要帮助构建一个算法,该算法计算某个帐户在特定时刻是否从未达到定义的值(假设为1000)。

让我们看看以下样本:

ID     Date             Value        Type    Account
----   -------------    ----------   -----   -------
1      2015-07-23       100.00       C       1
2      2015-07-28       350.00       C       1
3      2015-08-14       250.00       C       1
4      2015-08-30       180.00       C       1
5      2015-09-22       230.00       C       1
6      2015-09-28       230.00       D       1

在这种情况下,当余额为1110.00时,第一笔借记交易就发生了。因此,即使现在当前余额低于1000.00,我也需要考虑此帐户

ID     Date             Value        Type    Account
----   -------------    ----------   -----   ---------
1      2015-07-23       190.00       C       2
2      2015-07-28       350.00       C       2
3      2015-08-14       450.00       C       2
4      2015-08-30       100.00       D       2
5      2015-09-22       100.00       C       2

在这种情况下,有一个借记交易在达到1000.00之前取消了余额。所以我不应该考虑这个帐户。

是否有任何通用且简单的方法来进行此计算?

谢谢!

编辑:根据评论,这是我到目前为止:

decimal counter = 0;
bool hasBonus = false;
foreach ( var tx in txList ) {
    if ( tx.TransactionType == TransactionType.C ) {
        counter += tx.Value;
    }
    else if ( tx.TransactionType == TransactionType.D ) {
        counter -= tx.Value;
    }
    if ( counter >= 1000M ) {
        hasBonus = true;
    }
}

4 个答案:

答案 0 :(得分:2)

假设你有一个交易类

public enum TransactionType 
  {
    C,
    D
  }

public class Transaction
{
    public int Id {get; set;}
    public DateTime Date{get;set;}
    public double Value{get;set;}
    public TransactionType Type{get;set;}
    public int Account{get;set;}
}

正如你所说,你从数据库中获取它们以便覆盖部分。您获得IEnumerable<Transaction>。使用以下函数可以解决这个问题:

public bool AccountIsGood(IEnumerable<Transaction> dbTransactions)
{
    var transactions = dbTransactions.OrderBy(t => t.Date).ToList();
    var sum = 0;
    foreach(var tran in transactions)
    {

       if(tran.Type = TransactionType.D)
       {
          return false;
       }
       sum += tran.Value;

       if(sum > 1000)
       {
         return true;
       }
   }
  return false;
}

修改:C#中更优化的解决方案是,如果您使用以下代码传递IQueryable<Transaction>代替IEnumarable<Transaction>,则可以将事务拆分为批量处理:< / p>

public bool AccountIsGood(IQueryable<Transaction> dbTransactions)
{
    var transactions = dbTransactions.OrderBy(t => t.Date);
   // transactions is now and OrderedQueryable
    var sum = 0M;
    var totalTrans = transactions.Count();
    var skip = 0;
    while(skip < totalTrans)
    {
       foreach(var tran in transactions.Skip(skip).Take(100).ToList())
       {

          if(tran.Type = TransactionType.D)
          {
             sum -= tran.Value;
          }
          else
          {
             sum += tran.Value;
          }

          if(sum > 1000M)
          {
            return true;
         }
       }
     }
     skip += 100;
   }
 return false;
}

如果你可以在数据库中移动它,一次又一次地删除到DB的行程,那么更好的解决方案就是

答案 1 :(得分:1)

这是使用LINQ的解决方案:

var transactions = new[]
                    {
                        new { Value = 100.0, IsCredit = true, Account = 1 },
                        new { Value = 350.0, IsCredit = true, Account = 1 },
                        new { Value = 250.0, IsCredit = true, Account = 1 },
                        new { Value = 180.0, IsCredit = true, Account = 1 },
                        new { Value = 230.0, IsCredit = true, Account = 1 },
                        new { Value = 230.0, IsCredit = false, Account = 1 },
                        new { Value = 190.0, IsCredit = true, Account = 2 },
                        new { Value = 350.0, IsCredit = true, Account = 2 },
                        new { Value = 450.0, IsCredit = true, Account = 2 },
                        new { Value = 100.0, IsCredit = false, Account = 2 },
                        new { Value = 100.0, IsCredit = true, Account = 2 },
                    };

var bonusStatusOfAccounts = transactions.GroupBy(
    t => t.Account,
    t => t,
    (account, accountTransactions) =>
    new
        {
            Account = account,
            HasBonus = accountTransactions.Aggregate(
                new { AccountBalance = 0.0, HasBonus = false },
                (state, t) =>
                    {
                        var newBalance = state.AccountBalance + (t.IsCredit ? t.Value : -t.Value);
                        return new
                            {
                                AccountBalance = newBalance,
                                HasBonus = state.HasBonus || newBalance >= 1000
                            };
                    },
                state => state.HasBonus)
        }).ToList();

通过帐户对交易进行分组,我们每个交易账户都有一个IEnumerable,其中有足够的信息来判断HasBonus是否应该为真。

Aggregate()的通用形式有三个参数:

  1. 一个起始状态(在这种情况下,帐户余额最初为零,HasBonus为假)

  2. 代表&#34;添加&#34;这个状态的一个交易(这里我计算新余额并设置HasBonus,如果它&gt; s&gt; = 1000)

  3. 代表进入最终状态并从中得到我们想要的答案(这里只是获取HasBonus标志)

  4. 通过调整第二个代表中的逻辑,您可以精确控制奖励的条件。

答案 2 :(得分:1)

您的代码看起来很好解决您的问题。但是,在你的循环内部,你应该在达到平衡阈值后立即中断,以避免多余的计算:

if ( counter >= 1000M ) {
    hasBonus = true;
    // Stop iterating through transactions.
    break;
}

以良好的性能解决问题的真正关键在于您如何阅读交易数据以及如何使其可供其他组件访问。

确保以Enumerable的形式返回交易,并使用yield return ...从您的读取循环返回单个交易。

当您使用实体框架时,只要您不执行ToList()ToArray()Count()或类似内容,您就不必担心这一点这在代码中的某个地方实现了事务集合。

答案 3 :(得分:1)

  

是否有任何通用且简单的方法来进行此计算?

使用此模型,编号

IMO,唯一可以改进的是可用性

假设模型是这样的

public enum TransactionType { Credit, Debit }

public class Transaction
{
    public int ID { get; set; }
    public DateTime Date { get; set; }
    public decimal Value { get; set; }
    public TransactionType Type { get; set; }
    public bool IsCredit {  get { return Type == TransactionType.Credit; } }
    public int Account { get; set; }
}

我会将计算放在像这样的辅助函数

public static class TransactionUtils
{
    public static IEnumerable<KeyValuePair<Transaction, decimal>> GetCreditInfo(this IEnumerable<Transaction> accountTransactions)
    {
        decimal credit = 0;
        return from t in accountTransactions
               orderby t.Date, t.ID
               select new KeyValuePair<Transaction, decimal>(t, credit += t.IsCredit ? t.Value : -t.Value);
    }
}

现在可以使用LINQ查询来回答不同的问题,包括帖子中的原文。

例如,让我们拿你的样本数据

var transactions = new List<Transaction>
{
    new Transaction { ID = 1, Date = new DateTime(2015, 07, 23), Value = 100, Type = TransactionType.Credit, Account = 1 },
    new Transaction { ID = 2, Date = new DateTime(2015, 07, 28), Value = 350, Type = TransactionType.Credit, Account = 1 },
    new Transaction { ID = 3, Date = new DateTime(2015, 08, 14), Value = 250, Type = TransactionType.Credit, Account = 1 },
    new Transaction { ID = 4, Date = new DateTime(2015, 08, 30), Value = 180, Type = TransactionType.Credit, Account = 1 },
    new Transaction { ID = 5, Date = new DateTime(2015, 09, 22), Value = 230, Type = TransactionType.Credit, Account = 1 },
    new Transaction { ID = 6, Date = new DateTime(2015, 09, 28), Value = 230, Type = TransactionType.Debit, Account = 1 },

    new Transaction { ID = 1, Date = new DateTime(2015, 07, 23), Value = 190, Type = TransactionType.Credit, Account = 2 },
    new Transaction { ID = 2, Date = new DateTime(2015, 07, 28), Value = 350, Type = TransactionType.Credit, Account = 2 },
    new Transaction { ID = 3, Date = new DateTime(2015, 08, 14), Value = 450, Type = TransactionType.Credit, Account = 2 },
    new Transaction { ID = 4, Date = new DateTime(2015, 08, 30), Value = 100, Type = TransactionType.Debit, Account = 2 },
    new Transaction { ID = 5, Date = new DateTime(2015, 09, 22), Value = 100, Type = TransactionType.Credit, Account = 2 },
};

回答原始问题就像这样

decimal maxCredit = 1000;

针对特定帐户

int account = 1;
bool hasBonus = transactions
    .Where(t => t.Account == account)
    .GetCreditInfo().Any(info => info.Value >= maxCredit);

适用于所有帐户

var bonusInfo = transactions.GroupBy(t => t.Account, (key, elements) => new
{ 
    Account = key,
    HasBonus = elements.GetCreditInfo().Any(info => info.Value >= maxCredit)
}).ToList();

其他

var maxCreditInfo = transactions.GroupBy(t => t.Account, (key, elements) => new
{
    Account = key,
    MaxCredit = elements.GetCreditInfo().Max(info => info.Value)
}).ToList();

var bonusTransactionInfo = transactions.GroupBy(t => t.Account, (key, elements) => new
{
    Account = key,
    BonusTransactions = elements.GetCreditInfo()
        .Where(info => info.Key.IsCredit && info.Value >= maxCredit).ToList()
}).ToList();