从 C# 中的一对求和函数中找到可实现的最低值

问题描述 投票:0回答:1

该规范的目的是为 40 圈比赛找到进站和轮胎配方的最佳组合。任何一组圈数都不能超过 25 圈,因为此时模型汽车的燃油将耗尽。我手动编写的大部分步骤,对于一次进站,没有太多可能的组合,但对于两次进站(具有 3 组不同的圈数),手动编写是没有意义的。尽管同时,除了写出来之外,我想不出任何方法......

这是迄今为止的完整脚本(使用十进制而不是数学论文中的双精度):

using System;
using System.Collections.Generic;
using System.Linq;
                    
public class Program
{
    public static void Main()
    {
        List<decimal> findMin = new List<decimal>();

        //Add all possible values to the list 
        for(int i = 25; i >= 20; i--)
        {
            findMin.Add(LowestCombo(i, 40 - i));
        }

        Console.WriteLine(" ");

        //Print lowest number in the list
        Console.WriteLine($"The lowest possible time achievable is {findMin.Min()}");

    }
    public static decimal LowestCombo (int a, int b){
        List<decimal> sumBoth = new List<decimal>();
        string[] identifier = {"all soft tyres", "all medium tyres", "soft, then medium", "medium, then soft"};

        //Add all possible combinations of the two to the list
        sumBoth.Add(SoftSummation(a) + SoftSummation(b));
        sumBoth.Add(MedSummation(a) + MedSummation(b));
        sumBoth.Add(SoftSummation(a) + MedSummation(b));
        sumBoth.Add(MedSummation(a) + SoftSummation(b));
        
        //Print the combination of laps and tyres as well as the time, then return the lowest time
        Console.WriteLine($"The lowest possible time achievable with {a} initial laps and {b} final laps for a 1 pit stop race is {sumBoth.Min()}, {identifier[sumBoth.IndexOf(sumBoth.Min())]}.");
        return sumBoth.Min();
    }

    
    public static decimal SoftSummation(int a)
    {   
        decimal sum = 0;
        
            for(int x = 1; x <= a; x++)
            {
                //Tyre degradation function for the soft compound (Math.Pow can't take decimal??)
                sum += (0.1262m * (x * x * x * x)) - (4.476m * (x * x * x)) + (56.37m * (x * x)) - (152.9m * x) + (1.427m * (100000m));
            }
        
        return sum; 
    }

    
    public static decimal MedSummation(int a)
    {   
        decimal sum = 0;
        
            for(int x = 1; x <= a; x++)
            {
                //Tyre degradation function for the medium compound
                sum += (0.8406m * (x * x)) + (44.77m * x) + (1.434m * 100000m);
            }
        
        return sum; 
    }
}

对最初模糊的问题表示歉意,这里有一些澄清(仍然有点难以解释,但我希望它比原来的问题更有意义):

该脚本旨在通过检查进站和轮胎配方的不同组合来找到 40 圈比赛中可能达到的最短时间。它的作用本质上是将总比赛距离(40 圈)分为两部分:具有一定圈数的初始部分和具有剩余圈数的最终部分。

任何一个分段都不能超过 25 圈,因此只留下 20 种不同的可能组合(5 种不同的分段可能组合,以及 4 种不同的轮胎胶组合)。因为没有那么多组合,所以我基本上只是写出了轮胎化合物的所有可能组合,并循环测试了所有可能的段组合。

不过,我还需要考虑两进站策略,它将整个比赛分为 3 个部分,使得仅仅“写出所有组合”不太可行。我想知道是否有更好的编写方法,甚至可以自动化整个过程。

c# function sum combinations
1个回答
0
投票

您试图解决的基本问题是一种通用方法,用于为任意数量的段获取每个段的项目的有效组合,并限制每个段的总数和最大项目。我认为这是一个值得尝试解决的有趣问题,并且我发现了一些可行的方法,但这是一个复杂的解决方案。

using System;
using System.Collections.Generic;
using System.Linq;
                    
public class Program
{
    public static void Main()
    {
        var segments = 3;
        //You don't specify a minimum lap count, but it may make sense
        //to have one. If you want the minimum to be 1 you can just
        //change that here.
        var minLapsPerSegment = 5;
        var maxLapsPerSegment = 25;
        var totalLaps = 40;
        var lapsPerSegment = new int[segments];
        //all combinations already tried. Using HashSet to optimize for "IsSubset" calls
        var combinations = new List<HashSet<int>>();
        //initialize all laps to minLapsPerSegment
        for(var i = 0; i < lapsPerSegment.Length; i++)
        {
            lapsPerSegment[i] = minLapsPerSegment;
        }
        //we will methodically increment lap counts to get all combinations
        //of valid laps per segment, but once the first lap count is over the
        //max lap count, that's where it ends.
        while(lapsPerSegment[0] <= maxLapsPerSegment)
        {
            //each pass we will increment through all possible lap counts
            //for the second to last segment, and then figure out what the last
            //segment lap count has to be for that count combination
            //after looping through all possibilities for the second to last segment
            //we will bubble up through the previous segments incrementing them so that
            //we get all combinations
            for(var laps = minLapsPerSegment; laps <= maxLapsPerSegment; laps++)
            {
                lapsPerSegment[segments - 2] = laps;
                var lastSegmentLapCount = getLastSegmentLapCount(lapsPerSegment, totalLaps);
                //if the beginning laps already add up to the total laps such that
                //the last segment lap count has to be less than the minimum, break now
                //because the last segment will only get smaller
                if (lastSegmentLapCount < minLapsPerSegment)
                {
                    break;
                }
                //this is not a valid combination of laps, so continue onto the next
                if (lastSegmentLapCount > maxLapsPerSegment)
                {
                    continue;
                }
                lapsPerSegment[segments - 1] = lastSegmentLapCount;
                var currentCombination = lapsPerSegment.ToHashSet();
                //if the current combination of lap counts is a subset of any of the previous
                //lap counts, then this is just a permutation of segments that have already been
                //checked, so we can skip this combination.
                if (combinations.Any(x => currentCombination.IsSubsetOf(x)))
                {
                    continue;
                }
                //now we have a valid combination of laps per segment. This is where you would
                //do any of your lap time calculations with the different tire types, but for
                //this example I'm just going to print the valid combination of laps per segment
                Console.WriteLine(String.Join(',',lapsPerSegment));
                combinations.Add(lapsPerSegment.ToHashSet());
            }
            //edge case if we only have two segments, then after this first loop we are done
            //otherwise this would go into an infinite loop
            if (segments == 2)
            {
                break;
            }
            var previousIndexToModify = segments - 3;
            while(previousIndexToModify >= 0)
            {
                lapsPerSegment[previousIndexToModify]++;
                //if the previous segment has maxed out, roll it back to min lap count and bubble up to the previous segment
                //before that so that we increment through all the possible combinations
                if(lapsPerSegment[previousIndexToModify] > maxLapsPerSegment && previousIndexToModify != 0)
                {
                    lapsPerSegment[previousIndexToModify] = minLapsPerSegment;
                    previousIndexToModify--;
                }
                else
                {
                    //we've incremented a previous segment, so time to loop again and test all combinations
                    //with these beginning lap segment counts.
                    break;
                }
            }
        }
        Console.WriteLine(combinations.Count);
    }
    
    private static int getLastSegmentLapCount(int[] lapsPerSegment, int totalLaps)
    {
        var sumOfBeginningLaps = 0;
        for(var i = 0; i < lapsPerSegment.Length - 1; i++)
        {
            sumOfBeginningLaps += lapsPerSegment[i];
        }
        var lastSegmentLapCount = totalLaps - sumOfBeginningLaps;
        return lastSegmentLapCount;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.