X.
https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=176388
you have 1 meter walkroad, and randomly generate rain, the rain is 1 cm. simulate how many rain drops to cover all the 1 meter [-0.01~1].
struct interv {
double left=0, right=0.01;
bool isWet() {. 1point3acres
return left>=right;
}
};
就好了,然后生成0,1之间的一个均匀分布,模拟每个雨滴看落到哪个interval里,直
到100个都打湿。
用merge intervals
Google – Rain Drop Problem
有一个1m长的side walk, 假设是0到1。雨滴的宽度为0.01m,每次下雨的位置随机,雨滴和雨滴之间可能no overlap, complete overlap和partial overlap。问多少雨滴才能覆盖整个side walk。
(1) 首先这道题我在模拟的过程中碰到一个问题,因为double精度的关系,在[0.0, 1.0]之间相当于有"无数"个点,导致在模拟过程中下了几十万滴雨都没有把整个side walk覆盖。因为想象一下,比如现在下了2滴雨, range分别为
[0.2000000000000001, 0.2100000000000001]
[0.2200000000000001, 0.2300000000000001]
但是其实这两滴雨之间还有"无数"个点没有被覆盖。
所以我觉得这道题应该要保留两位小数,这样像上面那两滴雨已经算把0.20 ~ 023这段完全覆盖了。
(2) 另一个需要保留两位小数的理由就是Java的double type真是能虐死人。
所以我们需要等概率的来random[-0.01, 1.0]的之间的数并保留两位小数。方法有点类似于rand2 to rand6,保证概率的一致性。
[Solution]
模拟的过程有两种解法
__[Solution #1]__
merge interval + rand2 to rand6 + binary search
思路就是维护一个list of interval, 每次random一个新的雨滴,通过binary search来找插入位置。然后merge.
注意这道题的merge并不像一般的merge interval一样,因为这道题确定每个新的interval range都是0.01, 所以找到插入点后只需要merge前后两个就好了。
但是就算这样,其实这道题merge interval并不好做,因为比如[0.20, 0.21]和[0.22, 0.23]这两个interval就要merge了,而不是非要curr.start = prev.end才能和前面merge。另外binary search也是难写的一比。
后面有merge interval的code,但是真不知道对不对,binary search也偷懒了直接linear search了。
__[Solution #2]__
后来发现如果保留两位小数的话,其实0.0到1.0之间就是100个点。我们完全可以避免double带来的一堆麻烦。也可以不用merge来merge去的。
直接开个size为100的array作为bucket,每次random一个[-1, 100]的数,那么雨滴范围就是[start, start + 1]。看start和start + 1的bucket是否为空,是的话,bucket置1,bucketCnt++。 直到bucketCnt为100为止。
[期望]
网上看来的:
一共100个buckets, 假设现在剩余x个buckets为空,那么
雨滴落在这x个buckets里的概率为x / 100,这样剩余的buckets就为x – 1
雨滴没有落在这x个buckets里的概率为(100 – x) / 100,剩余的buckets仍然为x
那么
f(x) = f(x – 1) * (x / 100) + f(x) * (100 – x) / 100 + 1
100 * f(x) = x * f(x – 1) + (100 – x) * f(x) + 100
f(x) = f(x – 1) + 100 / x
f(x) = 100 * (1 + 1/2 + 1/3 + 1/4 + … + 1/x),后面那一串称为调和级数。
那么对于这道题来说,期望就是f(100) = 518.737752
这道题如果真被问到,模拟过程的算法肯定是重点。
期望的话可以说个大概的数字。能分析出概率情况写出上面第一个式子已经非常不错了。调和级数不调和级数这种东西,面试官是不会期待有人能答出来的。
Read full article from Google – Rain Drop Problem
落雨滴
第五轮是 有一个0-1的线条,然后有一雨点掉下来(position),这个雨点掉在这个线条上会散开成 0.01 m 长的范围,然后设计两个api,一个是掉雨点,一个是查询当前的线条是不是都已经被雨点覆盖了
思路:
见帖子 雨滴落到线条https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=176388
you have 1 meter walkroad, and randomly generate rain, the rain is 1 cm. simulate how many rain drops to cover all the 1 meter [-0.01~1].
- static class Interval {
- double left = 0, right = 0.01;
- boolean isWet() {
- return left >= right;
- }
- }
- public static void main(String[] args) {
- simulateRainDrop();
- }
- public static void simulateRainDrop() {
- Interval[] sidewalk = new Interval[100];
- for (int i = 0; i < 100; i++) {
- sidewalk[i] = new Interval();
- }
- int cnt = 0, wetCnt = 0;
- while (wetCnt < 100) {
- ++cnt;
- double p = Math.random();
- double left = p - 0.005;
- double right = p + 0.005;
- if (left >= 0) {
- int idx = (int) (left / 0.01);
- if (!sidewalk[idx].isWet()) {
- double iright = left - idx * 0.01;
- if (iright < sidewalk[idx].right) {
- sidewalk[idx].right = iright;
- if (sidewalk[idx].isWet())
- ++wetCnt;
- }
- }
- }
- if (right <= 1) {
- int idx = (int) (right / 0.01);
- if (!sidewalk[idx].isWet()) {
- double ileft = right - idx * 0.01;
- if (ileft > sidewalk[idx].left) {
- sidewalk[idx].left = ileft;
- if (sidewalk[idx].isWet())
- ++wetCnt;
- }
- }
- }
- }
- System.out.println(cnt);
- }
struct interv {
double left=0, right=0.01;
bool isWet() {. 1point3acres
return left>=right;
}
};
就好了,然后生成0,1之间的一个均匀分布,模拟每个雨滴看落到哪个interval里,直
到100个都打湿。
vector<interv> sidewalk(100,interv()); int cnt=0, wetCnt=0, idx; while(wetCnt<100) { ++cnt; double p= (double)(rand())/RAND_MAX; double left=p-0.005; double right=p+0.005; if(left>=0) { idx=left/0.01; if(!sidewalk[idx].isWet()) { iright=left-idx*0.01; if (iright<sidewalk[idx].right) { sidewalk[idx].right=iright; if(sidewalk[idx].wet()) ++wetCnt; } } } if(right<=1) { idx=right/0.01; if(!sidewalk[idx].wet()) { ileft=right-idx*0.01; if (ileft>sidewalk[idx].left) { sidewalk[idx].left=ileft; if(sidewalk[idx].isWet()) ++wetCnt; } } } } |
Google – Rain Drop Problem
有一个1m长的side walk, 假设是0到1。雨滴的宽度为0.01m,每次下雨的位置随机,雨滴和雨滴之间可能no overlap, complete overlap和partial overlap。问多少雨滴才能覆盖整个side walk。
- 模拟整个下雨过程,返回总共需要的雨滴数
- 算期望值。
(1) 首先这道题我在模拟的过程中碰到一个问题,因为double精度的关系,在[0.0, 1.0]之间相当于有"无数"个点,导致在模拟过程中下了几十万滴雨都没有把整个side walk覆盖。因为想象一下,比如现在下了2滴雨, range分别为
[0.2000000000000001, 0.2100000000000001]
[0.2200000000000001, 0.2300000000000001]
但是其实这两滴雨之间还有"无数"个点没有被覆盖。
所以我觉得这道题应该要保留两位小数,这样像上面那两滴雨已经算把0.20 ~ 023这段完全覆盖了。
(2) 另一个需要保留两位小数的理由就是Java的double type真是能虐死人。
0.09 + 0.01 = 0.09999999999999999(3) 还有一点需要注意的是,这道题random雨滴的范围不应该是[0.0, 1.0],而应该是[-0.01, 1.0],因为雨滴宽度的关系,如果random范围是[0.0, 1.0]的话,那么0.0被覆盖的概率和其他点被覆盖的概率是不一样的,只有当random到0.0的时候才会被覆盖,但是比如1.0这个点,random到0.99和1.0都能被覆盖。
所以我们需要等概率的来random[-0.01, 1.0]的之间的数并保留两位小数。方法有点类似于rand2 to rand6,保证概率的一致性。
1.01 * Math.random() – 0.01
[Solution]
模拟的过程有两种解法
__[Solution #1]__
merge interval + rand2 to rand6 + binary search
思路就是维护一个list of interval, 每次random一个新的雨滴,通过binary search来找插入位置。然后merge.
注意这道题的merge并不像一般的merge interval一样,因为这道题确定每个新的interval range都是0.01, 所以找到插入点后只需要merge前后两个就好了。
但是就算这样,其实这道题merge interval并不好做,因为比如[0.20, 0.21]和[0.22, 0.23]这两个interval就要merge了,而不是非要curr.start = prev.end才能和前面merge。另外binary search也是难写的一比。
后面有merge interval的code,但是真不知道对不对,binary search也偷懒了直接linear search了。
__[Solution #2]__
后来发现如果保留两位小数的话,其实0.0到1.0之间就是100个点。我们完全可以避免double带来的一堆麻烦。也可以不用merge来merge去的。
直接开个size为100的array作为bucket,每次random一个[-1, 100]的数,那么雨滴范围就是[start, start + 1]。看start和start + 1的bucket是否为空,是的话,bucket置1,bucketCnt++。 直到bucketCnt为100为止。
[期望]
网上看来的:
一共100个buckets, 假设现在剩余x个buckets为空,那么
雨滴落在这x个buckets里的概率为x / 100,这样剩余的buckets就为x – 1
雨滴没有落在这x个buckets里的概率为(100 – x) / 100,剩余的buckets仍然为x
那么
f(x) = f(x – 1) * (x / 100) + f(x) * (100 – x) / 100 + 1
100 * f(x) = x * f(x – 1) + (100 – x) * f(x) + 100
f(x) = f(x – 1) + 100 / x
f(x) = 100 * (1 + 1/2 + 1/3 + 1/4 + … + 1/x),后面那一串称为调和级数。
那么对于这道题来说,期望就是f(100) = 518.737752
这道题如果真被问到,模拟过程的算法肯定是重点。
期望的话可以说个大概的数字。能分析出概率情况写出上面第一个式子已经非常不错了。调和级数不调和级数这种东西,面试官是不会期待有人能答出来的。
class
Solution {
int
N =
100
;
public
int
rainDrop() {
Random rand =
new
Random();
boolean
[] buckets =
new
boolean
[N];
int
cnt =
0
;
int
bucketCount =
0
;
while
(
true
) {
int
randStart = rand.nextInt(N) -
1
;
int
randEnd = randStart +
1
;
cnt++;
if
(randStart >=
0
&& !buckets[randStart]) {
buckets[randStart] =
true
;
bucketCount++;
}
else
if
(!buckets[randEnd]) {
buckets[randEnd] =
true
;
bucketCount++;
}
if
(bucketCount == N) {
break
;
}
}
return
cnt;
}
}
class
Solution2 {
public
int
rainDrop() {
List<Interval> intervals =
new
ArrayList<>();
int
cnt =
0
;
while
(
true
) {
double
randStart =
1.01
* Math.random() -
0.01
;
randStart = round(randStart);
double
randEnd = randStart +
0.01
;
randEnd = round(randEnd);
Interval newInterval =
new
Interval(randStart, randEnd);
int
index = binarySearch(intervals, newInterval);
mergeIntervals(intervals, index, newInterval);
if
(intervals.size() ==
1
&& intervals.get(
0
).start <=
0.0
&& intervals.get(
0
).end >=
1.0
) {
break
;
}
else
{
cnt++;
}
}
return
cnt;
}
private
int
binarySearch(List<Interval> intervals, Interval newInterval) {
for
(
int
i =
0
; i < intervals.size(); i++) {
if
(newInterval.start <= intervals.get(i).start) {
return
i;
}
}
return
intervals.size();
}
private
void
mergeIntervals(List<Interval> intervals,
int
index, Interval newInterval) {
Interval curr = newInterval;
if
(intervals.isEmpty()) {
intervals.add(newInterval);
}
else
if
(index ==
0
) {
if
(newInterval.start == intervals.get(
0
).start) {
return
;
}
else
if
(round(newInterval.end +
0.01
) >= intervals.get(
0
).start) {
intervals.get(
0
).start = Math.min(intervals.get(
0
).start, newInterval.start);
}
else
{
intervals.add(
0
, newInterval);
}
}
else
if
(index == intervals.size()) {
Interval last = intervals.get(intervals.size() -
1
);
if
(round(last.end +
0.01
) >= newInterval.start) {
last.end = Math.max(last.end, newInterval.end);
}
else
{
intervals.add(newInterval);
}
}
else
{
Interval prev = intervals.get(index -
1
);
Interval next = intervals.get(index);
if
(round(prev.end +
0.01
) < newInterval.start && round(newInterval.end +
0.01
) < next.start) {
intervals.add(index, newInterval);
}
else
{
if
(round(prev.end +
0.01
) >= newInterval.start && round(newInterval.end +
0.01
) >= next
.start) {
prev.end = next.end;
intervals.remove(next);
}
else
if
(round(prev.end +
0.01
) >= newInterval.start) {
prev.end = Math.max(prev.end, newInterval.end);
if
(round(prev.end +
0.01
) >= next.start) {
prev.end = next.end;
intervals.remove(next);
}
}
else
if
(round(newInterval.end +
0.01
) >= next.start) {
next.start = Math.min(newInterval.start, next.start);
if
(round(prev.end +
0.01
) >= next.start) {
prev.end = next.end;
intervals.remove(next);
}
}
else
{
intervals.add(index, newInterval);
}
}
}
}
private
double
round(
double
d) {
return
(
double
) Math.round(d *
100
) /
100
;
}
private
class
Interval {
double
start;
double
end;
Interval(
double
start,
double
end) {
this
.start = start;
this
.end = end;
}
}
}