问题1
求任意浮点数的算术平方根,要求精度小于eps = 1e-6。
心路历程
自己写的一起合成,就是一个简单的二分,然后0-1之间的浮点数的求法也考虑了。面试官看了后让处理一下边界条件的情况,想了想没看出来是什么问题。
n = 0
l, r = 0, max(1, n)
eps = 1e-6
while l < r:
mid = (l + r) / 2
if abs(mid ** 2 - n) < eps:
break
if mid ** 2 - n > 0:
r = mid
else:
l = mid
print(mid - n ** 0.5)
print(mid - n ** 0.5 < eps)
面试官提醒了一下,说让把 n = 0 的答案输出一下,并且看一下精度是否满足 eps。
输出看了一下答案,没满足精度
0.0009765625
False
想了想就是在while循环里边的break的判断有问题,应该是 abs(mid - n ** 0.5) < eps 就好了,但是在这里我们不能使用 n ** 0.5,面试的时候想了半天都没想到应该怎么解决,最后面试官提醒了一下才知道,还是刷题太少了。代码如下:
n = 0
l, r = 0, max(1, n)
eps = 1e-6
while l < r:
mid = (l + r) / 2
if r - l < eps:
break
if mid ** 2 - n > 0:
r = mid
else:
l = mid
print(mid - n ** 0.5)
print(abs(mid - n ** 0.5) < eps)
输出为:
4.76837158203125e-07
True
问题2
给定一个圆,在当前圆中随机抽取一个点
心路历程
想的是分别按照 半径 和 角度 按照均匀分布做抽样,然后再按照 sin 和 cos 根据半径和圆心求出抽样的结果,然后就按照思路写了一下。
import random
import math
import matplotlib.pyplot as plt
import numpy as np
def plot_circle(center=(3, 3), r=2):
x = np.linspace(center[0] - r, center[0] + r, 5000)
y1 = np.sqrt(r ** 2 - (x - center[0]) ** 2) + center[1]
y2 = -np.sqrt(r ** 2 - (x - center[0]) ** 2) + center[1]
plt.plot(x, y1, c='k')
plt.plot(x, y2, c='k')
r = 1
x, y = 1, 1
draw_ans = [[], []]
point_numbers = 5000
for _ in range(5000):
t1 = random.uniform(0, r)
t2 = random.uniform(0, math.pi * 2)
yy = t1 * math.sin(t2) + x
xx = t1 * math.cos(t2) + y
draw_ans[0].append(xx)
draw_ans[1].append(yy)
plot_circle((x, y), r)
plt.scatter(draw_ans[0], draw_ans[1]) # x 代表x轴 y 代表y轴数据, 数据维度必须相同
plt.show()
res = 0
R = math.sqrt(r * r / 2)
for i in range(len(draw_ans[0])):
if (draw_ans[0][i] - x) ** 2 + (draw_ans[1][i] - y) ** 2 <= R * R:
res += 1
print(res/point_numbers)
面试官看了之后说这样的抽取并不是均匀的,然后让可视化出来,发现更靠近圆心地方的点更加的密集。
随后我又尝试输出了一下整个圆面积一半的同心圆内落入的点的数量占比,就是上边代码的最后几行,发现结果为 0.71,并不是0.5,所以这样子的随机抽样是不对的。
想了想不知道怎么搞,然后面试官提醒,可以将整个圆分成很多个同心圆,让相邻的圆的面积的差相等,然后在每个圆中在做抽样,有了思路然后实现,代码如下:
import random
import math
import matplotlib.pyplot as plt
import numpy as np
def plot_circle(center=(3, 3), r=2):
x = np.linspace(center[0] - r, center[0] + r, 5000)
y1 = np.sqrt(r ** 2 - (x - center[0]) ** 2) + center[1]
y2 = -np.sqrt(r ** 2 - (x - center[0]) ** 2) + center[1]
plt.plot(x, y1, c='k')
plt.plot(x, y2, c='k')
r = 1
x, y = 1, 1
k = 100
r1 = math.sqrt(r * r / k)
r_list = [0, r1]
for i in range(2, k + 1):
r_list.append(r1 * math.sqrt(i))
kth = random.randint(0, k - 1)
t1 = random.uniform(r_list[kth], r_list[kth + 1])
t2 = random.uniform(0, math.pi * 2)
yy = t1 * math.sin(t2) + x
xx = t1 * math.cos(t2) + y
print(xx, yy)
draw_ans = [[], []]
point_numbers = 10000
for _ in range(point_numbers):
kth = random.randint(0, k - 1)
t1 = random.uniform(r_list[kth], r_list[kth + 1])
t2 = random.uniform(0, math.pi * 2)
yy = t1 * math.sin(t2) + x
xx = t1 * math.cos(t2) + y
draw_ans[0].append(xx)
draw_ans[1].append(yy)
plot_circle((x, y), r)
plt.scatter(draw_ans[0], draw_ans[1]) # x 代表x轴 y 代表y轴数据, 数据维度必须相同
plt.show()
res = 0
R = math.sqrt(r * r / 2)
for i in range(len(draw_ans[0])):
if (draw_ans[0][i] - x) ** 2 + (draw_ans[1][i] - y) ** 2 <= R * R:
res += 1
print(res/point_numbers)
上述代码是将整个圆分成了一百个同心圆然后再做的抽样。
随后我又尝试输出了一下整个圆面积一半的同心圆内落入的点的数量占比,就是上边代码的最后几行,发现结果为 0.5。
然后面试官又说了,现在的实现还是近似的,能不能直接给出抽样的结果。想了一会儿不知道怎么搞(我是真的菜
然后面试官提醒,将k看作无穷大,然后再想想。
r_list 变成了 ,所以对半径做抽样的时候就直接可以写成 math.sqrt(random.uniform(0, 1)) * r 即可,所以最后的代码如下:
import random
import math
import matplotlib.pyplot as plt
import numpy as np
def plot_circle(center=(3, 3), r=2):
x = np.linspace(center[0] - r, center[0] + r, 5000)
y1 = np.sqrt(r ** 2 - (x - center[0]) ** 2) + center[1]
y2 = -np.sqrt(r ** 2 - (x - center[0]) ** 2) + center[1]
plt.plot(x, y1, c='k')
plt.plot(x, y2, c='k')
r = 1
x, y = 1, 1
t1 = math.sqrt(random.uniform(0, 1)) * r
t2 = random.uniform(0, math.pi * 2)
yy = t1 * math.sin(t2) + x
xx = t1 * math.cos(t2) + y
print(xx, yy)
draw_ans = [[], []]
point_numbers = 10000
for _ in range(point_numbers):
t1 = math.sqrt(random.uniform(0, 1)) * r
t2 = random.uniform(0, math.pi * 2)
yy = t1 * math.sin(t2) + x
xx = t1 * math.cos(t2) + y
draw_ans[0].append(xx)
draw_ans[1].append(yy)
plot_circle((x, y), r)
plt.scatter(draw_ans[0], draw_ans[1])
plt.show()
res = 0
R = math.sqrt(r * r / 2)
for i in range(len(draw_ans[0])):
if (draw_ans[0][i] - x) ** 2 + (draw_ans[1][i] - y) ** 2 <= R * R:
res += 1
print(res/point_numbers)