Skip to content

Instantly share code, notes, and snippets.

@masukomi
Forked from Gorcenski/fizzbuzz.py
Last active May 23, 2022 23:55
Show Gist options
  • Save masukomi/74f6a2e525f0d073510e510738533225 to your computer and use it in GitHub Desktop.
Save masukomi/74f6a2e525f0d073510e510738533225 to your computer and use it in GitHub Desktop.
The most obnoxious solution to FizzBuzz I can imagine.
# Emily Gorcensky's "obnoxious" fizz-buzz (less-compact version) annotated for non-python geeks.
# Explanation via Twitter: https://twitter.com/EmilyGorcenski/status/1228407309656903680?s=20
# If you want to dive into the meanings of how it works: it models fizzbuzz by
# computing the isomorphism of the finite cyclic groups of the values of the
# fizzes and buzzes and whatnot.
#
# These abelian groups are mapped to the unit circle in the complex plane and
# Represented as roots of unity. Such a interpetation has a polynomial
# representation. Therefore, the cartesian product in the isomorphism is
# represented as polynomial multiplication. The coefficients of a Polynomial
# multiplication can be represented as a cauchy product, which is equivalent to a
# 1-D vector covolution.
#
# We can then take the Nth roots of unity and evaluate them against the
# individual polynomial factors of the polynomial representing...
#
# The cyclic group generated by its factors. This is also a Sylow subgroup of the
# cyclic group generated by the product of the values of the fizzes and buzzes.
# So we can find this subgroup using the lcm
import numpy as np
from functools import reduce
class Buzzer:
def __init__(self, **kwargs):
values = [v for k, v in kwargs.items()]
# values in this case will be
# [3,5,7,11]
self.kwargs = kwargs
# lcm Returns the Lowest Common Multiple of |x1| and |x2|
# np.lcm.reduce([3, 12, 20]) -> 60
self.lcm = np.lcm.reduce(values) # -> 1155
self.eps = 1e-7
#
self.p = self.polybuilder(
# The reduce(fun,seq) fuction is used to apply a particular
# function passed in its argument to all of the list elements
# mentioned in the sequence passed along.
reduce(
#lambda arguments : expression
#
#convolve: Returns the discrete, linear convolution of two one-dimensional sequences.
# convolution: np.convolve([1, 2, 3], [0, 1, 0.5])
# produces: array([0. , 1. , 2.5, 4. , 1.5])
# see: https://www.quora.com/How-do-I-find-n-in-the-convolution-of-two-sequences
lambda q, r : np.convolve(q, r),
# passing the following values to polyvec
# 385, 231, 105, 165
[self.polyvec(self.lcm // v) for v in values],
[1]), self.lcm)
# takes a number ("order") and returns
# [[1], <array of num -1 zeroes>, [-1] ]
@staticmethod
def polyvec(order): # order will be: 385, 231, 105, or 165
# numpy.concatenate((a1, a2, ...), axis=0, out=None)
# Join a sequence of arrays along an existing axis.
# a = np.array([[1, 2], [3, 4]])
# b = np.array([[5, 6]])
#
# np.concatenate((a, b), axis=0)
# array([[1, 2],
# [3, 4],
# [5, 6]])
return np.concatenate(([1],
# zeroes: Return a new array of given shape and type, filled with zeros.
# np.zeros(5) -> array([ 0., 0., 0., 0., 0.])
np.zeros(order - 1), # an array of 384, 230, 104, or 164 zeroes
[-1]))
@staticmethod
def polybuilder(f, lcm):
return lambda x : sum(
# numpy.array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0)
# Create an array.
# f is either
# * the reduce function from the initializer
# * polyvec from the fizzbuzz call
f * np.array(
# exp: Calculate the exponential of all elements in the input array.
# numpy.exp(x, /, out=None, *, where=True, casting='same_kind',
# order='K', dtype=None, subok=True[, signature, extobj]) = <ufunc 'exp'>
#
# j reperesents the "imaginary" unit: https://en.wikipedia.org/wiki/Imaginary_unit
# discussion re changing it to i in python:
# https://bugs.python.org/issue10562
[np.exp(1j * x * k * 2 * np.pi / lcm)
for k in range(len(f) - 1, -1, -1)]))
def fizzbuzz(self, i):
return reduce(
lambda x, y : x + y,
# k is "fizz", "buzz", "baz", and "bar"
# k * bool(...) -> k (if true) or "" (if false)
[k * bool(
# numpy.absolute(x, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj]) = <ufunc 'absolute'>
# Calculate the absolute value element-wise.
#
# np.abs is a shorthand for this function.
# x = np.array([-1.2, 1.2])
# np.absolute(x) -> array([ 1.2, 1.2])
np.abs(
self.polybuilder(
self.polyvec(self.lcm // v),
self.lcm)(i)) < self.eps) # lcm -> Lowest Common Multiple again
for k, v in self.kwargs.items()],
str(i) * bool(
np.abs(self.p(i)) > self.eps))
print([Buzzer(fizz=3, buzz=5, baz=7, bar=11).fizzbuzz(i) for i in range(100)])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment