该规范的目的是为 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 个部分,使得仅仅“写出所有组合”不太可行。我想知道是否有更好的编写方法,甚至可以自动化整个过程。
您试图解决的基本问题是一种通用方法,用于为任意数量的段获取每个段的项目的有效组合,并限制每个段的总数和最大项目。我认为这是一个值得尝试解决的有趣问题,并且我发现了一些可行的方法,但这是一个复杂的解决方案。
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;
}
}