将列表中小于 1% 的数字设置为 1%,同时保持总数为 100%

Setting number in list that's less than 1% to 1% while keeping total 100%

我 运行 我的 Web 应用程序存在逻辑问题。

背景:页面上会出现一排 div。他们的 CSS width: 百分比等于项目的 int 值 div 与列表的总 int 值的百分比。

问题:我不希望任何宽度小于 1%。这意味着我必须将低于 1% 的项目的宽度增加到 1%,并减少所有其他项目的宽度,以便总百分比加起来正好达到 100%。

这会产生一个问题,即本来正好是 1% 的项目会减少到不到 1%。

我已经尝试了很多方法来做到这一点,但这是我当前的迭代。通过将它放入控制台应用程序,我为自己和你们简化了它。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PercentageOfTotal
{
    public class Item
    {
        public int Value { get; set; }
        public double Percent { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var items = new List<Item>();

            items.Add(new Item { Value = 26 });
            items.Add(new Item { Value = 31 });
            items.Add(new Item { Value = 47 });
            items.Add(new Item { Value = 175 });
            items.Add(new Item { Value = 50 });
            items.Add(new Item { Value = 1 });
            items.Add(new Item { Value = 74 });
            items.Add(new Item { Value = 8 });
            items.Add(new Item { Value = 219 });
            items.Add(new Item { Value = 169 });

            int sum = items.Sum(x => x.Value);
            double minPercentage = 0.01;

            System.Console.WriteLine("Value - Percent");

            foreach (var item in items)
            {
                item.Percent = item.Value / (double)sum;
                System.Console.WriteLine(item.Value + " - " + item.Percent);
            }

            System.Console.WriteLine("---------");
            System.Console.WriteLine(items.Sum(x => x.Value) + " - " + items.Sum(x => x.Percent));
            System.Console.WriteLine("\n");

            var itemsLessThanOnePercent = items.Where(x => x.Percent < minPercentage).ToList();
            var itemsGreaterThanOrEqualToOnePercent = items.Where(x => x.Percent >= minPercentage).ToList();
            double totalPercentageLessThanOne = itemsLessThanOnePercent.Sum(x => x.Percent);
            double reduceEachGreaterThanOneBy = ((itemsLessThanOnePercent.Count() * minPercentage) - totalPercentageLessThanOne) / itemsGreaterThanOrEqualToOnePercent.Count();

            foreach (var item in itemsGreaterThanOrEqualToOnePercent)
            {
                item.Percent = item.Percent - reduceEachGreaterThanOneBy;
            }

            foreach (var item in itemsLessThanOnePercent)
            {
                item.Percent = minPercentage;
            }

            System.Console.WriteLine("Value - Percent");

            foreach (var item in items)
            {
                System.Console.WriteLine(item.Value + " - " + item.Percent);
            }

            System.Console.WriteLine("---------");
            System.Console.WriteLine(items.Sum(x => x.Value) + " - " + items.Sum(x => x.Percent));
            System.Console.ReadLine();
        }
    }
}

如您所见,本例将 itemValue = 8 减少到小于 1% (0.009),因为 item.Value 一开始就已经是 1%。

基本上,我希望 item 的百分比相对于 item.Value 的总和,没有任何百分比小于 1%,并且百分比总和等于 100%。

如有任何帮助,我们将不胜感激。

您应该将项目计算为迭代,直到达到预期的百分比为止。你可以递归地做。我这样修改了你的代码;

    static void Main()
    {

        var items = new List<Item>();

        items.Add(new Item { Value = 26 });
        items.Add(new Item { Value = 31 });
        items.Add(new Item { Value = 47 });
        items.Add(new Item { Value = 175 });
        items.Add(new Item { Value = 50 });
        items.Add(new Item { Value = 1 });
        items.Add(new Item { Value = 74 });
        items.Add(new Item { Value = 8 });
        items.Add(new Item { Value = 219 });
        items.Add(new Item { Value = 169 });

        int sum = items.Sum(x => x.Value);
        double minPercentage = 0.01;

        System.Console.WriteLine("Value - Percent");

        foreach (var item in items)
        {
            item.Percent = item.Value / (double)sum;
            System.Console.WriteLine(item.Value + " - " + item.Percent);
        }

        System.Console.WriteLine("---------");
        System.Console.WriteLine(items.Sum(x => x.Value) + " - " + items.Sum(x => x.Percent));
        System.Console.WriteLine("\n");
        CalculatePercents(items,minPercentage);

        System.Console.WriteLine("Value - Percent");

        foreach (var item in items)
        {
            System.Console.WriteLine(item.Value + " - " + item.Percent);
        }

        System.Console.WriteLine("---------");
        System.Console.WriteLine(items.Sum(x => x.Value) + " - " + items.Sum(x => x.Percent));
        System.Console.ReadLine();

        Console.ReadLine();
    }

    private static void CalculatePercents(List<Item> items,double minPercentage)
    {

        var itemsLessThanOnePercent = items.Where(x => x.Percent < minPercentage).ToList();
        var itemsGreaterThanOrEqualToOnePercent = items.Where(x => x.Percent >= minPercentage).ToList();
        double totalPercentageLessThanOne = itemsLessThanOnePercent.Sum(x => x.Percent);
        double reduceEachGreaterThanOneBy = ((itemsLessThanOnePercent.Count() * minPercentage) - totalPercentageLessThanOne) / itemsGreaterThanOrEqualToOnePercent.Count();

        foreach (var item in itemsGreaterThanOrEqualToOnePercent)
        {
            item.Percent = item.Percent - reduceEachGreaterThanOneBy;
        }

        foreach (var item in itemsLessThanOnePercent)
        {
            item.Percent = minPercentage;
        }
        var minPercentagesItems = items.Where(x => x.Percent < minPercentage).ToList();
        if (minPercentagesItems.Count == 0)
        {
            return;
        }
        CalculatePercents(minPercentagesItems,minPercentage);
    }

您可以按 Value 递增的顺序枚举项目。对于每个项目,分配百分比,如果小于最小值 - 结转溢出以分配给其余项目。好吧,我承认这很难用语言来解释,所以这里是示例代码:

double overflow = 0;
int left = items.Count;
// loop in order of ascending value
foreach (var item in items.OrderBy(c => c.Value))
{
    // calculate part of overflow to assign to this item, based on total and how much items still left
    var overflowForItem = overflow / left;
    // assign a bit less than expected if there is overflow
    item.Percent = (item.Value - overflowForItem) / (double)sum;
    // reduce overflow (we just assigned part of it above)
    overflow -= overflowForItem;
    if (item.Percent < minPercentage) {                    
        overflow += (minPercentage - item.Percent) * sum;
        item.Percent = minPercentage;
    }                
    left--;                
}