- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我尝试解决 Hackerland Radio Transmitters programming challange .
总而言之,挑战如下:
Hackerland is a one-dimensional city with n houses, where each house i is located at some xi on the x-axis. The Mayor wants to install radio transmitters on the roofs of the city's houses. Each transmitter has a range, k, meaning it can transmit a signal to all houses ≤ k units of distance away.
Given a map of Hackerland and the value of k, can you find the minimum number of transmitters needed to cover every house?
我的实现如下:
package biz.tugay;
import java.util.*;
public class HackerlandRadioTransmitters {
public static int minNumOfTransmitters(int[] houseLocations, int transmitterRange) {
// Sort and remove duplicates..
houseLocations = uniqueHouseLocationsSorted(houseLocations);
int towerCount = 0;
for (int nextHouseNotCovered = 0; nextHouseNotCovered < houseLocations.length; ) {
final int towerLocation = HackerlandRadioTransmitters.findNextTowerIndex(houseLocations, nextHouseNotCovered, transmitterRange);
towerCount++;
nextHouseNotCovered = HackerlandRadioTransmitters.nextHouseNotCoveredIndex(houseLocations, towerLocation, transmitterRange);
if (nextHouseNotCovered == -1) {
break;
}
}
return towerCount;
}
public static int findNextTowerIndex(final int[] houseLocations, final int houseNotCoveredIndex, final int transmitterRange) {
final int houseLocationWeWantToCover = houseLocations[houseNotCoveredIndex];
final int farthestHouseLocationAllowed = houseLocationWeWantToCover + transmitterRange;
int towerIndex = houseNotCoveredIndex;
int loop = 0;
while (true) {
loop++;
if (towerIndex == houseLocations.length - 1) {
break;
}
if (farthestHouseLocationAllowed >= houseLocations[towerIndex + 1]) {
towerIndex++;
continue;
}
break;
}
System.out.println("findNextTowerIndex looped : " + loop);
return towerIndex;
}
public static int nextHouseNotCoveredIndex(final int[] houseLocations, final int towerIndex, final int transmitterRange) {
final int towerCoversUntil = houseLocations[towerIndex] + transmitterRange;
int notCoveredHouseIndex = towerIndex + 1;
int loop = 0;
while (notCoveredHouseIndex < houseLocations.length) {
loop++;
final int locationOfHouseBeingChecked = houseLocations[notCoveredHouseIndex];
if (locationOfHouseBeingChecked > towerCoversUntil) {
break; // Tower does not cover the house anymore, break the loop..
}
notCoveredHouseIndex++;
}
if (notCoveredHouseIndex == houseLocations.length) {
notCoveredHouseIndex = -1;
}
System.out.println("nextHouseNotCoveredIndex looped : " + loop);
return notCoveredHouseIndex;
}
public static int[] uniqueHouseLocationsSorted(final int[] houseLocations) {
Arrays.sort(houseLocations);
final HashSet<Integer> integers = new HashSet<>();
final int[] houseLocationsUnique = new int[houseLocations.length];
int innerCounter = 0;
for (int houseLocation : houseLocations) {
if (integers.contains(houseLocation)) {
continue;
}
houseLocationsUnique[innerCounter] = houseLocation;
integers.add(houseLocationsUnique[innerCounter]);
innerCounter++;
}
return Arrays.copyOf(houseLocationsUnique, innerCounter);
}
}
我很确定这个实现是正确的。但是请看函数中的细节: findNextTowerIndex 和 nextHouseNotCoveredIndex:它们会一一遍历数组!
我的一个测试如下:
static void test_01() throws FileNotFoundException {
final long start = System.currentTimeMillis();
final File file = new File("input.txt");
final Scanner scanner = new Scanner(file);
int[] houseLocations = new int[73382];
for (int counter = 0; counter < 73382; counter++) {
houseLocations[counter] = scanner.nextInt();
}
final int[] uniqueHouseLocationsSorted = HackerlandRadioTransmitters.uniqueHouseLocationsSorted(houseLocations);
final int minNumOfTransmitters = HackerlandRadioTransmitters.minNumOfTransmitters(uniqueHouseLocationsSorted, 73381);
assert minNumOfTransmitters == 1;
final long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start) + " milliseconds..");
}
input.txt 可以从 here 下载. (这不是这个问题中最重要的细节,但仍然..)所以我们有一个 73382 房屋的数组,我特意设置了发射器范围,所以我的方法循环了很多:
这是在我的机器上测试的示例输出:
findNextTowerIndex looped : 38213
nextHouseNotCoveredIndex looped : 13785
Took: 359 milliseconds..
我也有这个测试,它不断言任何东西,只是保持时间:
static void test_02() throws FileNotFoundException {
final long start = System.currentTimeMillis();
for (int i = 0; i < 400; i ++) {
final File file = new File("input.txt");
final Scanner scanner = new Scanner(file);
int[] houseLocations = new int[73382];
for (int counter = 0; counter < 73382; counter++) {
houseLocations[counter] = scanner.nextInt();
}
final int[] uniqueHouseLocationsSorted = HackerlandRadioTransmitters.uniqueHouseLocationsSorted(houseLocations);
final int transmitterRange = ThreadLocalRandom.current().nextInt(1, 70000);
final int minNumOfTransmitters = HackerlandRadioTransmitters.minNumOfTransmitters(uniqueHouseLocationsSorted, transmitterRange);
}
final long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start) + " milliseconds..");
}
我随机创建 400 个发射器范围,并运行程序 400 次。我将在我的机器中获得如下运行时间。
Took: 20149 milliseconds..
所以现在,我说,我为什么不使用二进制搜索而不是遍历数组,并将我的实现更改如下:
public static int findNextTowerIndex(final int[] houseLocations, final int houseNotCoveredIndex, final int transmitterRange) {
final int houseLocationWeWantToCover = houseLocations[houseNotCoveredIndex];
final int farthestHouseLocationAllowed = houseLocationWeWantToCover + transmitterRange;
int nextTowerIndex = Arrays.binarySearch(houseLocations, 0, houseLocations.length, farthestHouseLocationAllowed);
if (nextTowerIndex < 0) {
nextTowerIndex = -nextTowerIndex;
nextTowerIndex = nextTowerIndex -2;
}
return nextTowerIndex;
}
public static int nextHouseNotCoveredIndex(final int[] houseLocations, final int towerIndex, final int transmitterRange) {
final int towerCoversUntil = houseLocations[towerIndex] + transmitterRange;
int nextHouseNotCoveredIndex = Arrays.binarySearch(houseLocations, 0, houseLocations.length, towerCoversUntil);
if (-nextHouseNotCoveredIndex > houseLocations.length) {
return -1;
}
if (nextHouseNotCoveredIndex < 0) {
nextHouseNotCoveredIndex = - (nextHouseNotCoveredIndex + 1);
return nextHouseNotCoveredIndex;
}
return nextHouseNotCoveredIndex + 1;
}
我期待性能大幅提升,因为现在我最多会循环 log(N) 次,而不是 O(N).. 所以 test_01 输出:
Took: 297 milliseconds..
请记住,之前是 359 毫秒。对于 test_02:
Took: 18047 milliseconds..
所以我总是在 20 秒左右获得数组遍历实现的值,而对于二分搜索实现,我总是在 18 - 19 秒左右。
我期待使用 Arrays.binarySearch 获得更好的性能提升,但显然不是这样,这是为什么呢?我错过了什么?我是否需要一个超过 73382 的数组才能看到好处,还是无关紧要?
编辑#01
在@huck_cussler 发表评论后,我尝试将我拥有的数据集(使用随机数)加倍和三倍,并尝试运行 test02(当然,在测试本身中将数组大小增加三倍..)。对于线性实现,时间是这样的:
Took: 18789 milliseconds..
Took: 34396 milliseconds..
Took: 53504 milliseconds..
对于二分搜索实现,我得到的值如下:
Took: 18644 milliseconds..
Took: 33831 milliseconds..
Took: 52886 milliseconds..
最佳答案
您的时间包括从硬盘驱动器中检索数据。这可能会占用您的大部分运行时间。从您的时间中省略数据加载,以便更准确地比较您的两种方法。想象一下,如果它需要 18 秒,并且您将 18.644 与 18.789(0.77% 改进)而不是 0.644 与 0.789(18.38% 改进)进行比较。
如果你有一个线性运算 O(n),例如加载一个二进制结构,并且你将它与二进制搜索 O(log n) 结合起来,你最终会得到 O(n)。如果您相信大 O 表示法,那么您应该期望 O(n + log n) 与 O(2 * n) 没有显着差异,因为它们都减少到 O(n)。
此外,二分搜索可能比线性搜索执行得更好或更差,具体取决于塔之间的房屋密度。假设有 1024 座房屋,每 4 座房屋均匀分布有一座塔。线性搜索每塔需要 4 步,而二分搜索每塔需要 log2(1024)=10 步。
还有一件事……您的 minNumOfTransmitters
方法正在对从 test_01
和 test_02
传入的已排序数组进行排序。该诉诸步骤比您的搜索本身花费的时间更长,这进一步掩盖了您的两种搜索算法之间的时间差异。
======
我创建了一个小型计时类(class),以便更好地了解正在发生的事情。我已经从 minNumOfTransmitters 中删除了这行代码以防止它重新运行排序,并添加了一个 boolean 参数来选择是否使用您的二进制版本。它总计 400 次迭代的总和,分离出每个步骤。我系统上的结果表明,加载时间使排序时间相形见绌,而排序时间又使求解时间相形见绌。
Load: 22.565s
Sort: 4.518s
Linear: 0.012s
Binary: 0.003s
很容易看出优化最后一步对整体运行时间没有太大影响。
private static class Timing {
public long load=0;
public long sort=0;
public long solve1=0;
public long solve2=0;
private String secs(long millis) {
return String.format("%3d.%03ds", millis/1000, millis%1000);
}
public String toString() {
return " Load: " + secs(load) + "\n Sort: " + secs(sort) + "\nLinear: " + secs(solve1) + "\nBinary: " + secs(solve2);
}
public void add(Timing timing) {
load+=timing.load;
sort+=timing.sort;
solve1+=timing.solve1;
solve2+=timing.solve2;
}
}
static Timing test_01() throws FileNotFoundException {
Timing timing=new Timing();
long start = System.currentTimeMillis();
final File file = new File("c:\\path\\to\\xnpwdiG3.txt");
final Scanner scanner = new Scanner(file);
int[] houseLocations = new int[73382];
for (int counter = 0; counter < 73382; counter++) {
houseLocations[counter] = scanner.nextInt();
}
timing.load+=System.currentTimeMillis()-start;
start=System.currentTimeMillis();
final int[] uniqueHouseLocationsSorted = HackerlandRadioTransmitters.uniqueHouseLocationsSorted(houseLocations);
timing.sort=System.currentTimeMillis()-start;
start=System.currentTimeMillis();
final int minNumOfTransmitters = HackerlandRadioTransmitters.minNumOfTransmitters(uniqueHouseLocationsSorted, 73381, false);
timing.solve1=System.currentTimeMillis()-start;
start=System.currentTimeMillis();
final int minNumOfTransmittersBin = HackerlandRadioTransmitters.minNumOfTransmitters(uniqueHouseLocationsSorted, 73381, true);
timing.solve2=System.currentTimeMillis()-start;
final long end = System.currentTimeMillis();
return timing;
}
关于java - 为什么 Arrays.binarySearch 与遍历数组相比没有提高性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43620487/
我正在尝试将一个旧项目从使用 ArrayList 集合升级到 List。除了转换 ArrayList.BinarySearch 之外,一切都非常顺利。虽然 List 有相应的方法,但 ArrayLis
问题 我想自己在 Klant 的对象上实现 BinarySearch 方法,我该怎么做?Klant 有一些变量。 public class Klant { public String klantID;
这个问题在这里已经有了答案: How do I compare strings in Java? (23 个回答) 5年前关闭。 我是 java 的新程序员,我正在尝试使用 binarySearch
我正在尝试在 F# 中实现一个简单的二进制搜索,作为学习函数式编程的一种方式。我已经在我的主要语言 C# 中以命令式方式实现了它,但我认为通过在 F# 中以递归方式实现它,这将是一个很好的挑战,也是学
我有三个长度为 9 的字符串数组,我想看看它们是否都包含相同的名称。我必须在线性时间 O(NlogN) 内完成此操作。我的计划是对两个数组进行排序,然后使用二进制搜索来查找相似的名称。我的代码就像这样
我使用 xpath 从 xml 文件中填充了一个排序的日期列表(以 dd/mm/yyyy 格式存储为字符串)。 然而,当查询列表以查看列表中是否存在某个日期时,我总是得到否定结果(即不存在),即使我已
我正在开发一个 Java 项目 BinarySearch。我正在尝试创建并初始化一个变量 mid ,它将找出中间值,但它给我一个错误,你不能在这里声明一个变量。我也用 split 声明尝试过,但没有用
我是一名 Java 初学者,我正在尝试编写二进制搜索算法的实现代码。 这是我的代码: protected int doSearch(List list, int key) throws Sea
所以基本上,我知道对有序列表执行二进制搜索只是为了它起作用,所以这是我正在使用的列表: Integer[] x = {1, 2, 3, 4, 5, 6}; 所以基本上它可以找到一个整数,但是当我输入一
正如标题所暗示的,我正在编写一些代码来执行 Java 版本的二分搜索。但是,我的返回语句被忽略,函数反而返回最后一个“捕获所有”返回语句,如下所示: public int binarySear
我应该如何使用二进制搜索来查找排序数组中相邻数字之间的距离是否大于 N?例如: Input: 2 5 8 11 16 Distance: 4 所以我们应该得到邻居之间有这样的距离的答案。 (11 到
所以我想写一个代码来返回键所在的索引,或者如果它不存在,它应该在哪里。我错过了什么?min 为 0,max 为 size - 1,buf 已排序 int binarySearch(string buf
当我尝试编写以下行时: int foundIndex = Collections.binarySearch(keys, key); 它显示错误:参数化方法binarySearch(List>, K)类
function binarySearch(items, target){ var startIndex = 0, stopIndex = items.length-1, middle = Math.
尝试用 Java 编写 Python 风格的 bisect_right,并使用 List 参数的泛型类型: import java.util.*; class Util { /* eqv to
我又遇到了一些关于列表和二进制搜索的问题。一般来说,我有: type TMyArr = array [1..5] of Integer; PMyList = record Com
我正在尝试使用 java.util.Arrays 类的二分搜索(不区分大小写),但它无法搜索指定数组中存在的字符串。以下是程序: package com.test; import java.util.
这不起作用: List byteArrayList = .... ; Collections.binarySearch(byteArrayList, new ByteArrayComparator()
嗨,我正在制作一个程序,该程序采用一系列“术语”,每个术语都有自己的权重(长值)和查询(字符串值)。它应该从 .txt 文件中获取许多术语并将它们初始化为私有(private)“Terms”数组,然后
我很想知道当我使用 Arrays.binarySearch 而不进行排序时得到的答案背后的逻辑是什么。 int d[]={6,-4,12,0,-10}; int x=12; int y=Arrays.
我是一名优秀的程序员,十分优秀!