3b1b-videos/_2021/bertrands_paradox.py

184 lines
5.4 KiB
Python
Raw Normal View History

from manim_imports_ext import *
class RandomChordScene(Scene):
title = ""
radius = 3.5
n_samples = 1000
long_color = BLUE
short_color = WHITE
chord_width = 0.5
chord_opacity = 0.35
run_time = 20
def construct(self):
circle = self.circle = Circle(radius=self.radius)
circle.set_stroke(GREY_B, 3)
circle.to_edge(LEFT)
chords = self.get_chords(circle)
flash_chords = chords.copy()
flash_chords.set_stroke(width=3, opacity=1)
indicators = Group(*map(self.get_method_indicator, chords))
title = Text(self.title)
title.to_corner(UR)
self.add(circle, title)
for s, rt in (slice(0, 16), 8), (slice(18, None), self.run_time):
fraction = self.get_fraction([c.long for c in chords[s]])
fraction.match_x(title)
fraction.match_y(circle)
self.add(fraction)
self.play(
ShowIncreasingSubsets(chords[s]),
ShowSubmobjectsOneByOne(flash_chords[s]),
ShowSubmobjectsOneByOne(indicators[s]),
fraction.alpha_update,
rate_func=linear,
run_time=rt,
)
self.remove(flash_chords)
self.remove(indicators)
self.remove(fraction)
self.add(fraction)
self.wait()
# Embed
self.embed()
def get_fraction(self, data):
tex = Tex(
"{100", "\\over ", "100", "+", "100}", "= ", "0.500"
)
nl1 = Integer(100, edge_to_fix=ORIGIN) # Number of long chords
nl2 = Integer(100, edge_to_fix=RIGHT)
ns = Integer(100, edge_to_fix=LEFT) # Number of short chords
ratio = DecimalNumber(0, num_decimal_places=4)
fraction = VGroup(
nl1.move_to(tex[0]),
tex[1],
nl2.move_to(tex[2]),
tex[3],
ns.move_to(tex[4]),
tex[5],
ratio.move_to(tex[6], LEFT),
)
def update_fraction(frac, alpha):
subdata = data[:int(np.floor(alpha * len(data)))]
n_long = sum(subdata)
n_short = len(subdata) - n_long
frac[0].set_value(n_long)
frac[0].set_color(self.long_color)
frac[2].set_value(n_long)
frac[2].set_color(self.long_color)
frac[4].set_value(n_short)
frac[4].set_color(self.short_color)
if len(subdata) == 0:
frac[-2:].set_opacity(0)
else:
frac[-2:].set_opacity(1)
frac[6].set_value(n_long / len(subdata))
return frac
fraction.alpha_update = UpdateFromAlphaFunc(
fraction, update_fraction
)
return fraction
def get_chords(self, circle):
tri_len = np.sqrt(3) * self.radius
chords = VGroup(*(
self.get_random_chord(circle)
for x in range(self.n_samples)
))
for chord in chords:
chord.long = (chord.get_length() > tri_len)
chord.set_color(
self.long_color if chord.long else self.short_color
)
chords.set_stroke(
width=self.chord_width,
opacity=self.chord_opacity
)
return chords
def get_random_chord(self, circle):
return NotImplemented
def get_method_indicator(self, chord):
return NotImplemented
class PairOfPoints(RandomChordScene):
title = "Random pair of circle points"
def get_random_chord(self, circle):
return Line(
circle.pfp(random.random()),
circle.pfp(random.random()),
)
def get_method_indicator(self, chord):
dots = DotCloud([
chord.get_start(),
chord.get_end(),
])
dots.set_glow_factor(2)
dots.set_radius(0.25)
dots.set_color(YELLOW)
return dots
class CenterPoint(RandomChordScene):
title = "Random point in circle"
def get_random_chord(self, circle):
x = y = 1
while x * x + y * y > 1:
x = random.uniform(-1, 1)
y = random.uniform(-1, 1)
return self.chord_from_xy(x, y, circle)
def chord_from_xy(self, x, y, circle):
n2 = x * x + y * y
temp_x = math.sqrt(n2)
temp_y = math.sqrt(1 - n2)
line = Line(
[temp_x, -temp_y, 0],
[temp_x, temp_y, 0],
)
line.rotate(angle_of_vector([x, y, 0]), about_point=ORIGIN)
line.scale(circle.get_width() / 2, about_point=ORIGIN)
line.shift(circle.get_center())
return line
def get_method_indicator(self, chord):
dots = DotCloud([chord.get_center()])
dots.set_glow_factor(2)
dots.set_radius(0.25)
dots.set_color(YELLOW)
return dots
class RadialPoint(CenterPoint):
title = "Random point along radial line"
def get_random_chord(self, circle):
angle = random.uniform(0, TAU)
dist = random.uniform(0, 1)
return self.chord_from_xy(
dist * math.cos(angle),
dist * math.sin(angle),
circle
)
def get_method_indicator(self, chord):
dot = super().get_method_indicator(chord)
line = Line(self.circle.get_center(), dot.get_center())
line.set_length(self.radius, about_point=line.get_start())
line.set_stroke(YELLOW, 1)
return Group(dot, line)