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.

Revisions

  1. masukomi revised this gist Feb 18, 2020. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,24 @@
    # 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
  2. masukomi revised this gist Feb 14, 2020. 1 changed file with 76 additions and 15 deletions.
    91 changes: 76 additions & 15 deletions fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -1,32 +1,93 @@
    # Emily Gorcenski's "obnoxious" fizz-buzz (less-compact version) annotated for non-python geeks.
    # Emily Gorcensky's "obnoxious" fizz-buzz (less-compact version) annotated for non-python geeks.

    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
    self.lcm = np.lcm.reduce(values)
    # 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(reduce(lambda q, r : np.convolve(q, r),
    [self.polyvec(self.lcm // v) for v in values],
    [1]), self.lcm)
    #
    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):
    return np.concatenate(([1], np.zeros(order - 1), [-1]))

    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(f * np.array([np.exp(1j * x * k * 2 * np.pi / lcm)
    for k in range(len(f) - 1, -1, -1)]))
    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 * bool(np.abs(self.polybuilder(self.polyvec(self.lcm // v), self.lcm)(i)) < self.eps)
    for k, v in self.kwargs.items()],
    str(i) * bool(np.abs(self.p(i)) > self.eps))
    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)])
  3. masukomi renamed this gist Feb 14, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions ... fizzbuzz annotated for non-python geeks. → fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    # Emily Gorcenski's "obnoxious" fizz-buzz (less-compact version) annotated for non-python geeks.

    import numpy as np
    from functools import reduce

  4. masukomi renamed this gist Feb 14, 2020. 1 changed file with 0 additions and 0 deletions.
  5. @Gorcenski Gorcenski revised this gist Feb 12, 2020. 1 changed file with 23 additions and 18 deletions.
    41 changes: 23 additions & 18 deletions fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -2,24 +2,29 @@
    from functools import reduce

    class Buzzer:
    values = []
    lcm = 0
    polyvec = lambda cls, order : np.concatenate(([1], np.zeros(order - 1), [-1]))
    polybuilder = lambda cls, f : lambda x : sum(f * np.array([np.exp(1j * x * k * 2 * np.pi / cls.lcm)
    for k in range(len(f) - 1, -1, -1)]))
    eps = 1e-7
    p = None
    def __init__(self, **kwargs):
    values = [v for k, v in kwargs.items()]

    def fizzbuzz(cls, i, **kwargs):
    cls.values = cls.values if cls.values else [v for k, v in kwargs.items()]
    cls.lcm = cls.lcm if cls.lcm > 0 else reduce(lambda x, y: np.lcm(x, y), cls.values, 1)
    cls.p = cls.p if cls.p else cls.polybuilder(reduce(lambda q, r : np.convolve(q, r),
    [cls.polyvec(cls.lcm // v) for v in cls.values],
    [1]))

    self.kwargs = kwargs
    self.lcm = np.lcm.reduce(values)
    self.eps = 1e-7
    self.p = self.polybuilder(reduce(lambda q, r : np.convolve(q, r),
    [self.polyvec(self.lcm // v) for v in values],
    [1]), self.lcm)

    @staticmethod
    def polyvec(order):
    return np.concatenate(([1], np.zeros(order - 1), [-1]))

    @staticmethod
    def polybuilder(f, lcm):
    return lambda x : sum(f * np.array([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 * bool(np.abs(cls.polybuilder(cls.polyvec(cls.lcm // v))(i)) < cls.eps)
    for k, v in kwargs.items()],
    str(i) * bool(np.abs(cls.p(i)) > cls.eps))
    [k * bool(np.abs(self.polybuilder(self.polyvec(self.lcm // v), self.lcm)(i)) < self.eps)
    for k, v in self.kwargs.items()],
    str(i) * bool(np.abs(self.p(i)) > self.eps))

    print([Buzzer().fizzbuzz(i, fizz=3, buzz=5, baz=7, bar=11) for i in range(100)])
    print([Buzzer(fizz=3, buzz=5, baz=7, bar=11).fizzbuzz(i) for i in range(100)])
  6. @Gorcenski Gorcenski revised this gist Feb 12, 2020. 1 changed file with 20 additions and 15 deletions.
    35 changes: 20 additions & 15 deletions fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -1,20 +1,25 @@
    import numpy as np
    from functools import reduce

    def fizzbuzz(i, **kwargs):
    values = [v for k, v in kwargs.items()]
    lcm = reduce(lambda x, y: np.lcm(x, y), values, 1)
    polyvec = lambda order : np.concatenate(([1], np.zeros(order - 1), [-1]))
    polybuilder = lambda f : lambda x : sum(f * np.array([np.exp(1j * x * k * 2 * np.pi / lcm)
    for k in range(len(f) - 1, -1, -1)]))
    p = polybuilder(reduce(lambda q, r : np.convolve(q, r),
    [polyvec(lcm // v) for v in values],
    [1]))

    class Buzzer:
    values = []
    lcm = 0
    polyvec = lambda cls, order : np.concatenate(([1], np.zeros(order - 1), [-1]))
    polybuilder = lambda cls, f : lambda x : sum(f * np.array([np.exp(1j * x * k * 2 * np.pi / cls.lcm)
    for k in range(len(f) - 1, -1, -1)]))
    eps = 1e-7
    return reduce(lambda x, y : x + y,
    [k * bool(np.abs(polybuilder(polyvec(lcm // v))(i)) < eps)
    for k, v in kwargs.items()],
    str(i) * bool(np.abs(p(i)) > eps))
    p = None

    print([fizzbuzz(i, fizz=3, buzz=5, baz=7, bar=11) for i in range(100)])
    def fizzbuzz(cls, i, **kwargs):
    cls.values = cls.values if cls.values else [v for k, v in kwargs.items()]
    cls.lcm = cls.lcm if cls.lcm > 0 else reduce(lambda x, y: np.lcm(x, y), cls.values, 1)
    cls.p = cls.p if cls.p else cls.polybuilder(reduce(lambda q, r : np.convolve(q, r),
    [cls.polyvec(cls.lcm // v) for v in cls.values],
    [1]))

    return reduce(lambda x, y : x + y,
    [k * bool(np.abs(cls.polybuilder(cls.polyvec(cls.lcm // v))(i)) < cls.eps)
    for k, v in kwargs.items()],
    str(i) * bool(np.abs(cls.p(i)) > cls.eps))

    print([Buzzer().fizzbuzz(i, fizz=3, buzz=5, baz=7, bar=11) for i in range(100)])
  7. @Gorcenski Gorcenski revised this gist Feb 12, 2020. 1 changed file with 17 additions and 16 deletions.
    33 changes: 17 additions & 16 deletions fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -1,19 +1,20 @@
    import numpy as np
    def fizzbuzz(i, fizz=3, buzz=5):
    polybuilder = lambda f : lambda x : sum(f * np.power([x] * len(f),
    range(len(f) - 1, -1, -1)))
    F = np.zeros(fizz + 1); F[-1] = -1; F[0] = 1
    B = np.zeros(buzz + 1); B[-1] = -1; B[0] = 1
    from functools import reduce

    p = polybuilder(np.convolve(F, B))
    q = polybuilder(F)
    r = polybuilder(B)
    def fizzbuzz(i, **kwargs):
    values = [v for k, v in kwargs.items()]
    lcm = reduce(lambda x, y: np.lcm(x, y), values, 1)
    polyvec = lambda order : np.concatenate(([1], np.zeros(order - 1), [-1]))
    polybuilder = lambda f : lambda x : sum(f * np.array([np.exp(1j * x * k * 2 * np.pi / lcm)
    for k in range(len(f) - 1, -1, -1)]))
    p = polybuilder(reduce(lambda q, r : np.convolve(q, r),
    [polyvec(lcm // v) for v in values],
    [1]))

    eps = 1e-7
    return reduce(lambda x, y : x + y,
    [k * bool(np.abs(polybuilder(polyvec(lcm // v))(i)) < eps)
    for k, v in kwargs.items()],
    str(i) * bool(np.abs(p(i)) > eps))

    eps = 1e-13
    z = np.exp(1j * i * 2 * np.pi / np.lcm(fizz, buzz))
    return (str(i) * bool(np.abs(p(z)) > eps) + \
    'fizz' * bool(np.abs(q(z)) < eps) + \
    'buzz' * bool(np.abs(r(z)) < eps)
    )

    [fizzbuzz(i) for i in range(100)]
    print([fizzbuzz(i, fizz=3, buzz=5, baz=7, bar=11) for i in range(100)])
  8. @Gorcenski Gorcenski created this gist Aug 9, 2018.
    19 changes: 19 additions & 0 deletions fizzbuzz.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    import numpy as np
    def fizzbuzz(i, fizz=3, buzz=5):
    polybuilder = lambda f : lambda x : sum(f * np.power([x] * len(f),
    range(len(f) - 1, -1, -1)))
    F = np.zeros(fizz + 1); F[-1] = -1; F[0] = 1
    B = np.zeros(buzz + 1); B[-1] = -1; B[0] = 1

    p = polybuilder(np.convolve(F, B))
    q = polybuilder(F)
    r = polybuilder(B)

    eps = 1e-13
    z = np.exp(1j * i * 2 * np.pi / np.lcm(fizz, buzz))
    return (str(i) * bool(np.abs(p(z)) > eps) + \
    'fizz' * bool(np.abs(q(z)) < eps) + \
    'buzz' * bool(np.abs(r(z)) < eps)
    )

    [fizzbuzz(i) for i in range(100)]