source: http://www.mwctoys.com/REVIEW_010413a.htm
source: http://www.mwctoys.com/REVIEW_010413a.htm

Пришло время оживить преданный забвению FizzBuzz. Попробуем найти самое компактное решение FizzBuzz на Python.


Условие задачи

Напишите программу, печатающую числа от 1 до 100. Вместо чисел кратных трём, программа должна печатать 'Fizz'. Вместо чисел кратных пяти - 'Buzz'. Если число кратно и трём, и пяти, программа должна печатать 'FizzBuzz'.

Основа

Начнём со стандартного, классического решения:

for i in range(1, 101):
    if i%3==0 and i%5==0:
        print('FizzBuzz')
    elif i%3==0:
            print('Fizz')
    elif i%5==0:
            print('Buzz')
    else:
        print(i)

Укладываем стандартное решение в стандартный однострочник

print('\n'.join('FizzBuzz' if i%3==0 and i%5==0 else 'Fizz' if i%3==0 else 'Buzz' if i%5==0 else str(i) for i in range(1, 101)))

Избавляемся от Join, приручаем Print

[print('FizzBuzz' if i%3==0 and i%5==0 else 'Fizz' if i%3==0 else 'Buzz' if i%5==0 else i) for i in range(1, 101)]

Добавляем срез, укрощаем if else

[print('FizzBuzz'[4 if i%3 else 0:4 if i%5 else 8] or i) for i in range(1, 101)]

Оптимизируем срез, избавляемся от if else

[print('FizzBuzz'[i*i%3*4:8--i**4%5] or i) for i in range(1, 101)]

Сократили неплохо. Но, похоже с этой вариацией дальше не продвинуться. Пробуем иной вариант.

Заменяем срез конкатенацией, вертаем оператор modulo

[print('Fizz'*(i%3==0)+'Buzz'*(i%5==0) or i) for i in range(1, 101)]

Оптимизируем решение. Выравниваем по длине со срезом

[print((i%3<1)*'Fizz'+(i%5<1)*'Buzz' or i) for i in range(1, 101)]

Уходим в отрыв. Модифицируем окончательный вариант

[print(i%3//2*'Fizz'+i%5//4*'Buzz' or i+1) for i in range(100)]

А что по скорости?

print('\n'.join('FizzBuzz' if i%3==0 and i%5==0 else 'Fizz' if i%3==0 else 'Buzz' if i%5==0 else str(i) for i in range(1, 101))) # 2.53 msec
[print('FizzBuzz' if i%3==0 and i%5==0 else 'Fizz' if i%3==0 else 'Buzz' if i%5==0 else i) for i in range(1, 101)] # 8.11 msec
[print('FizzBuzz'[4 if i%3 else 0:4 if i%5 else 8] or i) for i in range(1, 101)] # 8.43 msec
[print('FizzBuzz'[i*i%3*4:8--i**4%5] or i) for i in range(1, 101)] # 8.31 msec
[print('Fizz'*(i%3==0)+'Buzz'*(i%5==0) or i) for i in range(1, 101)] # 8.38 msec
[print((i%3<1)*'Fizz'+(i%5<1)*'Buzz' or i) for i in range(1, 101)] # 8.4 msec
[print(i%3//2*'Fizz'+i%5//4*'Buzz' or i+1) for i in range(100)] # 8.49 msec

Методика расчёта: python -m timeit "выражение"


Вместо заключения

Помимо этой статьи, на Хабре мною опубликованы ещё пара статей на тему Python однострочников. Если интересно, ознакомиться можно по ссылкам ниже:

Всем спасибо.

Комментарии (5)


  1. SamXYZ
    05.12.2021 15:23
    +1

    Интересные способы решения,


  1. eandr_67
    05.12.2021 15:31
    +1

    А если избавиться от списка, будет ещё короче и, возможно, быстрее:

    for i in range(100):print(i%3//2*'Fizz'+i%5//4*'Buzz'or i+1)


    1. andreylw
      06.12.2021 11:16
      +1

      Нет быстрее не получилось, на уровне второго варианта


  1. lavagod
    05.12.2021 21:21
    +7

    Самое компактное решение не есть самое быстрое, имхо. Вот, например, вам решение тоже в одну строку. Не знаю как там по времени, но наверно тоже будет быстро:)

    print("1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\nFizz\n22\n23\nFizz\nBuzz\n26\nFizz\n28\n29\nFizzBuzz\n31\n32\nFizz\n34\nBuzz\nFizz\n37\n38\nFizz\nBuzz\n41\nFizz\n43\n44\nFizzBuzz\n46\n47\nFizz\n49\nBuzz\nFizz\n52\n53\nFizz\nBuzz\n56\nFizz\n58\n59\nFizzBuzz\n61\n62\nFizz\n64\nBuzz\nFizz\n67\n68\nFizz\nBuzz\n71\nFizz\n73\n74\nFizzBuzz\n76\n77\nFizz\n79\nBuzz\nFizz\n82\n83\nFizz\nBuzz\n86\nFizz\n88\n89\nFizzBuzz\n91\n92\nFizz\n94\nBuzz\nFizz\n97\n98\nFizz\nBuzz")


  1. desertkun
    06.12.2021 02:29

    del