今日のソリューションは前日よりも若干複雑になり、コードが少し増えましたが、それでも非常に簡単でした。
ソリューションの基本概念:
有効であるためには、パート 1 の次の条件を満たす必要があります:
数値がすべて増加しているのか、それとも次から次へと減少しているのかを判断する必要があります。
数値間のギャップは 3 を超えることはできません
var reports = File.ReadAllLines("./input1.txt") .Select(x => x.Split(" ")) .ToList(); Part1(reports); void Part1(List<string[]> input) { var validReports = input .Select(report => report.Select(int.Parse).ToList()) .Count(IsValidReport); Console.WriteLine(validReports); } bool IsValidReport(List<int> levels) { // We don't care about the numbers, just wether going up or down, not both var isIncreasing = IsIncreasing(levels); var isDecreasing = IsDecreasing(levels); if (!isIncreasing && !isDecreasing) return false; // Check that all adjacent levels differ by at least 1 and at most 3 for (var i = 0; i < levels.Count - 1; i++) { var diff = Math.Abs(levels[i + 1] - levels[i]); if (diff is < 1 or > 3) { return false; } } return true; } bool IsIncreasing(List<int> numbers) { for (var i = 1; i < numbers.Count; i++) { if (numbers[i] < numbers[i - 1]) return false; } return true; } bool IsDecreasing(List<int> numbers) { for (var i = 1; i < numbers.Count; i++) { if (numbers[i] > numbers[i - 1]) return false; } return true; }
簡単に説明すると、レポートをヘルパー IsValidReport() メソッドに渡す前に、LINQ を使用して文字列数値を整数に解析します。
このメソッドは、すべての数値が増加しているか減少しているかを確認します。これは、ID を順序付きリストと比較することによって行われ、すべての数値が一方向に進んでいることを証明します。
それらがすべて一方向 (バンドではなく) にあることがわかったら、各番号が互いに 3 つの番号以内にあるかどうかを確認できます。 Math.Abs() メソッドを使用すると、絶対数が返され、マイナスの数値を考慮するのに役立ちます。
注: 絶対数は、数値とゼロの間の数値です。つまり、-5 と 5 は両方とも 5 を返します。
パート 2 では、もう少し複雑な説明がありましたが、実際よりも難しく感じられます。
2 番目の部分では、レポートから 1 つの項目を削除しても有効なレポートと見なされるかどうかを知る必要があるという基本的な概念とともに、人的エラーに関する注意事項を紹介します。
void Part2(List<string[]> input) { var validReports = 0; foreach (var ints in input.Select(report => report.Select(int.Parse).ToList())) { // If valid as-is, count it if (IsValidReport(ints)) { validReports++; continue; } // Check if removing any single level makes it valid if (ints.Select((t, i) => ints.Where((_, index) => index != i).ToList()) .Any(IsValidReport)) { validReports++; } } Console.WriteLine(validReports); }
ここでは、LINQ の機能を最大限に活用し、文字列レポートを解析し、それを使用して ints 配列を変更しています。配列をループし、インデックスにある現在の項目を削除することにより、int の配列を変更します。
変更したら、変更したレポートをバリデーターを通じて実行します。これは、条件が満たされるとすぐ (つまり、変更されたレポートが有効になるとすぐに) ショートする Any() と組み合わせた LINQ Select() メソッドを使用して、一度に行うことができます。
これは、いずれかの項目を削除できればレポートは有効であるとみなされると記載されているためです。特定の項目が記載されていないため、一致する項目が見つかったらすぐに、削除された場合はレポートが有効であることを意味し、他のすべての項目の確認を停止できます。
次に、有効なレポートを追跡するために、validReports カウンタをインクリメントします。
def main(): # Read the input file and split each line into a list of strings with open("./input1.txt") as file: reports = [line.split() for line in file] part1(reports) part2(reports) def part1(reports): # Count valid reports using a list comprehension and the IsValidReport function valid_reports = 0 for report in reports: levels = list(map(int, report)) if is_valid_report(levels): valid_reports += 1 print(valid_reports) def part2(reports): valid_reports = 0 for report in reports: ints = list(map(int, report)) # If valid as-is, count it if is_valid_report(ints): valid_reports += 1 continue # Check if removing any single level makes it valid valid_with_dampener = False # Iterate over each element in ints for i in range(len(ints)): # Create a new list without the element at index i modified_report = [x for j, x in enumerate(ints) if j != i] # Check if the modified report is valid if is_valid_report(modified_report): valid_with_dampener = True break if valid_with_dampener: valid_reports += 1 print(valid_reports) def is_valid_report(levels): # Check if the sequence is either all increasing or all decreasing is_increasing = is_increasing_sequence(levels) is_decreasing = is_decreasing_sequence(levels) if not is_increasing and not is_decreasing: return False # Check that all adjacent levels differ by at least 1 and at most 3 for i in range(len(levels) - 1): diff = abs(levels[i + 1] - levels[i]) if diff < 1 or diff > 3: return False return True def is_increasing_sequence(numbers): for i in range(1, len(numbers)): if numbers[i] < numbers[i - 1]: return False return True def is_decreasing_sequence(numbers): for i in range(1, len(numbers)): if numbers[i] > numbers[i - 1]: return False return True if __name__ == "__main__": main()
より冗長でないソリューションの場合は、C# ソリューションの LINQ で行ったように、パート 2 の内部 for ループを変更とチェックに置き換えることができます。
var reports = File.ReadAllLines("./input1.txt") .Select(x => x.Split(" ")) .ToList(); Part1(reports); void Part1(List<string[]> input) { var validReports = input .Select(report => report.Select(int.Parse).ToList()) .Count(IsValidReport); Console.WriteLine(validReports); } bool IsValidReport(List<int> levels) { // We don't care about the numbers, just wether going up or down, not both var isIncreasing = IsIncreasing(levels); var isDecreasing = IsDecreasing(levels); if (!isIncreasing && !isDecreasing) return false; // Check that all adjacent levels differ by at least 1 and at most 3 for (var i = 0; i < levels.Count - 1; i++) { var diff = Math.Abs(levels[i + 1] - levels[i]); if (diff is < 1 or > 3) { return false; } } return true; } bool IsIncreasing(List<int> numbers) { for (var i = 1; i < numbers.Count; i++) { if (numbers[i] < numbers[i - 1]) return false; } return true; } bool IsDecreasing(List<int> numbers) { for (var i = 1; i < numbers.Count; i++) { if (numbers[i] > numbers[i - 1]) return false; } return true; }
しかし、同意していただけると思います。より適切な名前付き変数を使用した冗長メソッドの方がはるかに読みやすいです。
読んでいただきありがとうございます。明日もお付き合いください。そしていつものように twitter をフォローしてください
以上がAoC Day Red-Nosed レポート (C# および Python)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。