【问题标题】:Randomly Distribute Banknotes of different values to fit a total value随机分配不同价值的钞票以适合总价值
【发布时间】:2020-04-16 17:21:31
【问题描述】:

给定一个值=[1, 5, 10, 20, 50, 100] 的纸币数组, 什么是随机分配这些以适应总和的最佳方法,例如363? 我不想使用模数并通过先添加更大的音符来添加找到余数。我希望它更随机,这样它可能会有更自然的分布,其中纸币构成总和。

目前我正在这样做:

int temp = totalAmount;
for (int i = 0; i < values.Length; i++)
{
   amounts[i] = temp / values[i];
   temp %= values[i];
}

这会给我一个 3x100、1x50、1x10、3x1 的音符分布, 但我宁愿它是 1x100、3x50、2x20、6x10、13x13 之类的东西,虽然我想不出一个好的策略。

【问题讨论】:

    标签: c# arrays math


    【解决方案1】:

    如果你真的希望它是随机的,你可以这样做:

    var rnd = new Random();
    int currentValue = 0;
    var selectedNotes = new List<int>();
    
    int[] notes = {1, 5, 10, 20, 50, 100};
    int targetValue = 363;
    
    while (currentValue < targetValue)
    {
        int v = rnd.Next(0, notes.Length);
    
        if (currentValue + notes[v] > targetValue)
        {
            continue;
        }
    
        selectedNotes.Add(notes[v]);
        currentValue += notes[v];
    }
    
    foreach (var note in selectedNotes.GroupBy(z => z).Select(z => new {BankNote = z.Key, Quantity = z.Count()})
        .OrderByDescending(z => z.BankNote))
    {
        Console.WriteLine($"{note.Quantity}x{note.BankNote}");
    }
    

    【讨论】:

    • 我选择这个。它简单易用。谢谢您的帮助。它生成得很好!
    【解决方案2】:

    主要是为了挑战和看看这是怎么回事,我写了一个小类来计算所有可能的组合,然后你可以从这个桶中提取。
    作为奖励,这将允许从列表中提取任意数量的唯一组合(删除)。
    但是,我为每种笔记类型设置了限制,例如。 20 倍。如果没有任何限制,这将运行很长时间(或者可能会耗尽内存)。

    public static void Main()
    {
        var C = new Combinations();
        int targetValue = 363;
        int limitPerNote = 20;
    
        foreach(int v in new[] {1, 5, 10, 20, 50, 100})
        {
            int maxCnt = (int)(targetValue / v);
            maxCnt = Math.Min(maxCnt, limitPerNote);
            C.Add(v, maxCnt);
        }
    
        List<String> Results = new List<String>();
        while(!C.Inc())
        {
            if(C.TotalValue==targetValue)
            {
                //Console.WriteLine(C.CurrentCombinationToString());
                Results.Add(C.CurrentCombinationToString());
            }
        }
        Console.WriteLine($"{Results.Count} combinations found");
    
        Random random = new Random();
        Console.WriteLine($"Random combination:");
        Console.WriteLine(Results[random.Next(0, Results.Count)]);
    }
    
    public class Combinations
    {
        private List<CombinationItem> Items {get; set;} = new List<CombinationItem>();
    
        public void Add(int value, int maxCount)
        {
            Items.Add(new CombinationItem() {Value=value, MaxCount = maxCount});
        }
    
        public bool Inc()
        {
            int i = Items.Count -1;
            bool overflowFlag = true;
            while(overflowFlag)
            {
                overflowFlag = Items[i].Inc();
                if(overflowFlag) i--;
                if(i<0) return true; // final overflow, last combination reached
            }
            return false; // return without final overflow
        }
    
        public int TotalValue
        {get{ return Items.Sum((item) => item.Value * item.Count); }}
    
        public string CurrentCombinationToString()
        {
            return String.Join(" + ", Items.
            Where((item) => item.Count >0).
            Select((item) => $@"{item.Count}x {item.Value}"));
        }
    
    }
    
    public class CombinationItem
    {
        public int Value {get; set;}
        public int Count {get; set;}
        public int MaxCount {get; set;}
    
        public bool Inc()
        {
            // returns true on overflow
            Count++;
            if(Count > MaxCount)
            {
                Count=0;
                return true;
            }
            else
                return false;
        }
    }
    

    【讨论】:

    • 非常酷的解决方案。它看起来有点矫枉过正,能够在许多情况下实时执行此操作会很好。我看看效果如何
    【解决方案3】:

    这对你有用:

            Random random = new Random();
    
            int[] values = { 1, 5, 10, 20, 50, 100 };
            int[] amounts = new int[values.Length];
            int totalAmount = 363;
    
            int left = totalAmount;
            while (left > 0)
            {
                int nextNote = random.Next(0, values.Length);
                if (values[nextNote] < left)
                {
                    left -= values[nextNote];
                    amounts[nextNote]++;
                }
            }
    

    【讨论】:

    • amounts.Zip(values, (a,v) =&gt; a * v).Sum() == totalAmount //false
    • 做了一个小技巧,修好了,有点晚了,但还是修好了。
    猜你喜欢
    • 1970-01-01
    • 2021-02-18
    • 1970-01-01
    • 2017-03-24
    • 2021-10-25
    • 1970-01-01
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多