在有序列表中查找最接近的值

时间:2009-07-27 09:58:08

标签: java

我想知道如何编写一个简单的java方法,将closet Integer发现到已排序的Integer列表中的给定值。

这是我的第一次尝试:

public class Closest {

    private static List<Integer> integers = new ArrayList<Integer>();

    static {
        for (int i = 0; i <= 10; i++) {
            integers.add(Integer.valueOf(i * 10));
        }
    }

    public static void main(String[] args) {

        Integer closest = null;
        Integer arg = Integer.valueOf(args[0]);

        int index = Collections.binarySearch(
                integers, arg);

        if (index < 0) /*arg doesn't exist in integers*/ {
            index = -index - 1;
            if (index == integers.size()) {
                closest = integers.get(index - 1);
            } else if (index == 0) {
                closest = integers.get(0);
            } else {
                int previousDate = integers.get(index - 1);
                int nextDate =  integers.get(index);
                if (arg - previousDate < nextDate - arg) {
                    closest = previousDate;
                } else {
                    closest = nextDate;
                }
            }
        } else /*arg exists in integers*/ {
            closest = integers.get(index);
        }
        System.out.println("The closest Integer to " + arg + " in " + integers
                + " is " + closest);
    }
}

您对此解决方案有何看法?我相信有一种更清洁的方式来完成这项工作......

也许这样的方法存在于Java库的某个地方,我错过了它?

Manu

11 个答案:

答案 0 :(得分:32)

试试这个小方法:

public int closest(int of, List<Integer> in) {
    int min = Integer.MAX_VALUE;
    int closest = of;

    for (int v : in) {
        final int diff = Math.abs(v - of);

        if (diff < min) {
            min = diff;
            closest = v;
        }
    }

    return closest;
}

一些测试用例:

private final static List<Integer> list = Arrays.asList(10, 20, 30, 40, 50);

@Test
public void closestOf21() {
    assertThat(closest(21, list), is(20));
}

@Test
public void closestOf19() {
    assertThat(closest(19, list), is(20));
}

@Test
public void closestOf20() {
    assertThat(closest(20, list), is(20));
}

答案 1 :(得分:10)

科特琳很有帮助

fun List<Int>.closestValue(value: Int) = minBy { abs(value - it) }

val values = listOf(1, 8, 4, -6)

println(values.closestValue(-7)) // -6
println(values.closestValue(2)) // 1
println(values.closestValue(7)) // 8

列表无需进行顺便说一句

答案 2 :(得分:1)

我认为你所拥有的是最简单,最有效的方法。在排序列表中查找“最接近”的项目并不是编程中常见的内容(通常会查找较大的项目或较小的项目)。这个问题只对数字类型有意义,所以不是很普遍,因此有一个库函数是不常见的。

答案 3 :(得分:1)

为了解决这个问题,我用distanceTo方法扩展了Comparable Interface。 distanceTo的实现返回一个表示预期距离的double值,它与compareTo实现的结果兼容。

以下示例仅使用苹果来说明这个想法。您可以按重量,体积或甜度交换直径。袋子将始终返回“最接近的”苹果(最大的类似,怀疑或味道)

public interface ExtComparable<T> extends Comparable<T> {
   public double distanceTo(T other);
}

public class Apple implements Comparable<Apple> {
   private Double diameter;

   public Apple(double diameter) {
      this.diameter = diameter;
   }

   public double distanceTo(Apple o) {
      return diameter - o.diameter;
   }

   public int compareTo(Apple o) {
      return (int) Math.signum(distanceTo(o));
   }
}

public class AppleBag {
   private List<Apple> bag = new ArrayList<Apple>();

   public addApples(Apple...apples){
      bag.addAll(Arrays.asList(apples));
      Collections.sort(bag);
   }

   public removeApples(Apple...apples){
      bag.removeAll(Arrays.asList(apples));
   }

   public Apple getClosest(Apple apple) {
      Apple closest = null;
      boolean appleIsInBag = bag.contains(apple);
      if (!appleIsInBag) {
         bag.addApples(apple);
      }

      int appleIndex = bag.indexOf(apple);
      if (appleIndex = 0) {
         closest = bag.get(1);
      } else if(appleIndex = bag.size()-1) {
         closest = bag.get(bag.size()-2);
      } else {
         double absDistToPrev = Math.abs(apple.distanceTo(bag.get(appleIndex-1));
         double absDistToNext = Math.abs(apple.distanceTo(bag.get(appleIndex+1));
         closest = bag.get(absDistToNext < absDistToPrev ? next : previous);
      }

      if (!appleIsInBag) {
         bag.removeApples(apple);
      }

      return closest;
   }
}

答案 4 :(得分:1)

没有二进制搜索的解决方案(利用列表进行排序):

public int closest(int value, int[] sorted) {
  if(value < sorted[0])
    return sorted[0];

  int i = 1;
  for( ; i < sorted.length && value > sorted[i] ; i++);

  if(i >= sorted.length)
    return sorted[sorted.length - 1];

  return Math.abs(value - sorted[i]) < Math.abs(value - sorted[i-1]) ?
         sorted[i] : sorted[i-1];
}

答案 5 :(得分:0)

当然,你可以简单地使用for循环遍历并跟踪你所在的值和值之间的差异。它会看起来更干净,但要慢得多。

请参阅:Finding closest match in collection of numbers

答案 6 :(得分:0)

未经测试

int[] randomArray; // your array you want to find the closest
        int theValue; // value the closest should be near to

        for (int i = 0; i < randomArray.length; i++) {
            int compareValue = randomArray[i];

            randomArray[i] -= theValue;
        }

        int indexOfClosest = 0;
        for (int i = 1; i < randomArray.length; i++) {
            int compareValue = randomArray[i];

            if(Math.abs(randomArray[indexOfClosest] > Math.abs(randomArray[i]){
                indexOfClosest = i;
            }
        }

答案 7 :(得分:0)

我认为你的回答可能是返回单一结果的最有效方法。

但是,您的方法存在的问题是0(如果没有列表),1或2个可能的解决方案。当你有一个功能的两个可能的解决方案,你的问题真的开始:如果这不是最终的答案,但只是在一系列步骤中的第一个确定最佳的行动方案,以及你没有的答案怎么办?返回会提供更好的解决方案吗?唯一正确的做法是考虑两个答案并仅在最后比较进一步处理的结果。

将平方根函数看作是一个类似的问题。

答案 8 :(得分:0)

如果你没有大量关注性能(假设该组被搜索了两次),我认为使用Navigable设置可以获得更清晰的代码:

public class Closest
{
  private static NavigableSet<Integer> integers = new TreeSet<Integer>();

  static
  {
    for (int i = 0; i <= 10; i++)
    {
      integers.add(Integer.valueOf(i * 10));
    }
  }

  public static void main(String[] args)
  {
    final Integer arg = Integer.valueOf(args[0]);
    final Integer lower = integers.lower(arg);
    final Integer higher = integers.higher(arg);

    final Integer closest;
    if (lower != null)
    {
      if (higher != null)
        closest = (higher - arg > arg - lower) ? lower : higher;
      else
        closest = lower;
    }
    else
      closest = higher;

    System.out.println("The closest Integer to " + arg + " in " + integers + " is " + closest);
  }
}

答案 9 :(得分:0)

您的解决方案似乎渐近最优。如果它使用Math.min / max,它可能会稍微快一点(尽管可能不太可维护)。一个好的JIT可能具有使这些变得快速的内在函数。

int index = Collections.binarySearch(integers, arg);
if (index < 0) {
    int previousDate = integers.get(Math.max(0, -index - 2));
    int nextDate = integers.get(Math.min(integers.size() - 1, -index - 1));
    closest = arg - previousDate < nextDate - arg ? previousDate : nextDate;
} else {
    closest = integers.get(index);
}

答案 10 :(得分:0)

可能有点晚了,但这会起作用,这是一个数据结构二进制搜索:

科特林:

fun binarySearch(list: List<Int>, valueToCompare: Int): Int {
    var central: Int
    var initialPosition = 0
    var lastPosition: Int
    var centralValue: Int
    lastPosition = list.size - 1
    while (initialPosition <= lastPosition) {
        central = (initialPosition + lastPosition) / 2 //Central index
        centralValue = list[central]                   //Central index value
        when {
            valueToCompare == centralValue -> {
                return centralValue          //found; returns position
            }
            valueToCompare < centralValue -> {
                lastPosition = central - 1  //position changes to the previous index
            }
                else -> {
                initialPosition = central + 1 //position changes to next index
                }
            }
    }
    return -1 //element not found
}

Java:

public int binarySearch(int list[], int valueToCompare) {
    int central;
    int centralValue;
    int initialPosition = 0;
    int lastPosition = list . length -1;
    while (initialPosition <= lastPosition) {
        central = (initialPosition + lastPosition) / 2; //central index
        centralValue = list[central]; //central index value
        if (valueToCompare == centralValue) {
            return centralValue; //element found; returns position
        } else if (valueToCompare < centralValue) {
            lastPosition = central - 1; //Position changes to the previous index
        } else {
            initialPosition = central + 1; //Position changes to the next index
        }
        return -1; //element not found
    }
}

我希望这会有所帮助,祝您编码愉快。