int[] array = new int[5]{5,7,8,15,20};
int TargetNumber = 13;
ターゲット番号については、配列内の最も近い番号を見つけたいです。たとえば、ターゲット番号が13の場合、上記の配列でそれに最も近い番号は15です。C#でプログラムでこれを達成するにはどうすればよいですか?
編集:オーバーフロー問題を回避するために、以下のクエリを調整してlong
算術の使用に変換しました。
おそらく MoreLINQ のMinBy
メソッドを使用します。
var nearest = array.MinBy(x => Math.Abs((long) x - targetNumber));
または、couldだけを使用します:
var nearest = array.OrderBy(x => Math.Abs((long) x - targetNumber)).First();
...しかし、それはあなたが本当に必要としないコレクション全体をソートします。確かにsmall配列に対しては大きな違いはありません... re実際にしようとしています:いくつかの関数に従って最小値を持つ要素を見つけます。
配列が空の場合、これらの両方が失敗することに注意してください。したがって、最初に確認する必要があります。
.Net 3.5以降を使用している場合、LINQが役立ちます。
var closest = array.OrderBy(v => Math.Abs((long)v - targetNumber)).First();
または、独自の拡張メソッドを作成することもできます。
public static int ClosestTo(this IEnumerable<int> collection, int target)
{
// NB Method will return int.MaxValue for a sequence containing no elements.
// Apply any defensive coding here as necessary.
var closest = int.MaxValue;
var minDifference = int.MaxValue;
foreach (var element in collection)
{
var difference = Math.Abs((long)element - target);
if (minDifference > difference)
{
minDifference = (int)difference;
closest = element;
}
}
return closest;
}
次のように使用できます:
var closest = array.ClosestTo(targetNumber);
ジョンとリッチの両方が、MinBy
とClosestTo
で素晴らしい答えを出しました。ただし、単一の要素を見つけることが目的の場合は、OrderBy
の使用をお勧めしません。これらの種類のタスクにはあまりにも非効率的です。それは単に仕事にとって間違ったツールです。
以下は、MinByよりもわずかに優れたパフォーマンスを実現する手法で、既に.NETフレームワークに含まれていますが、MinByよりもエレガントではありません。 Aggregate
var nearest = array.Aggregate((current, next) => Math.Abs((long)current - targetNumber) < Math.Abs((long)next - targetNumber) ? current : next);
私が言ったように、ジョンの方法ほどエレガントではありませんが、実行可能です。
コンピューターのパフォーマンス:
この非常にセクシーなアプローチは、数年前にMath.NET Numericsで見つけました https://numerics.mathdotnet.com/ 配列のBinarySearchで動作します。補間の準備に役立ち、.Net 2.0まで機能しました。
public static int LeftSegmentIndex(double[] array, double t)
{
int index = Array.BinarySearch(array, t);
if (index < 0)
{
index = ~index - 1;
}
return Math.Min(Math.Max(index, 0), array.Length - 2);
}
パフォーマンスに関するカスタムコードの方が便利です。
public static int FindNearest(int targetNumber, IEnumerable<int> collection) {
var results = collection.ToArray();
int nearestValue;
if (results.Any(ab => ab == targetNumber))
nearestValue = results.FirstOrDefault(i => i == targetNumber);
else{
int greaterThanTarget = 0;
int lessThanTarget = 0;
if (results.Any(ab => ab > targetNumber)) {
greaterThanTarget = results.Where(i => i > targetNumber).Min();
}
if (results.Any(ab => ab < targetNumber)) {
lessThanTarget = results.Where(i => i < targetNumber).Max();
}
if (lessThanTarget == 0) {
nearestValue = greaterThanTarget;
}
else if (greaterThanTarget == 0) {
nearestValue = lessThanTarget;
}
else if (targetNumber - lessThanTarget < greaterThanTarget - targetNumber) {
nearestValue = lessThanTarget;
}
else {
nearestValue = greaterThanTarget;
}
}
return nearestValue;
}
平均に最も近い値を見つける必要がある場合
public static double Miidi(double[] list)
{
bool isEmpty = !list.Any();
if (isEmpty)
{
return 0;
}
else
{
double avg = list.Average();
double closest = 100;
double shortest = 100;
{
for ( int i = 0; i < list.Length; i++)
{
double lgth = list[i] - avg;
if (lgth < 0)
{
lgth = lgth - (2 * lgth);
}
else
lgth = list[i] - avg;
if (lgth < shortest)
{
shortest = lgth;
closest = list[i];
}
}
}
return closest;
}
}