Recently, a user named logo asked a question on the RL Discord server:

how to print numbers from 1 to 50 in python?

Little did I know, I was about to engage in one of the funniest conversation.

Pure Python approach

for i in range(50):
    print(i)

A very simple and straightforward solution. Wait, why can’t we just print(1,2,3,4,5,6,7,8,9,10...,50)?

“That’s too long” - user Ariel. “it’s better to do exec(f"print({','.join(str(i) for i in range(1,51))})")

Ok but?

i = 1
while True:
    print(i)
    i += 1
    if i > 50:
        break

Best method. Straight forward:

print(1)
print(2)
print(3)
.
.
.
.
print(50)

“next research question is how to do it with functional approach”? Oh right.

def getprint():
    return print
getprint()(1)
getprint()(2)
.
.
.
.
getprint()(50)

Or better: [print(i) for i in range(1,51)]

User James replied to the functional approach:

def recursive_print(start, end):
    def recursive_count_str(i):
        return f"{i}\n{recursive_count_str(i+1)}" if i < end else f"{i}"
    print(recursive_count_str(start))


recursive_print(1, 50)

Ok, can we do better?

class PrintFactory:
    def __init__(self, number):
        self.number = number
        
    def print_number(self):
        for i in range(self.number):
            print(i)

def print50():
    print_factory = PrintFactory(50)
    print_factory.print_number()

print50()

by OrganicPitaChips.

“I feel a strong yin yang struggle right now between my shitposting nature and the whole “admin” thing” - Ariel.

class ObjectOrientedPrintFactory:
    def __init__(self, number):
        self.number = number
        self.child_factory = None
        if number > 0:
            self.child_factory = ObjectOrientedPrintFactory(number - 1)
        
    def print_number(self):
        if self.child_factory:
            self.child_factory.print_number()
        print(self.number)

def print50():
    print_factory = ObjectOrientedPrintFactory(50)
    print_factory.print_number()

print50()

by hackeronsteriods.

Yeah this is from this link

Neural Network to print from 1 to 50

“I like how nobody actually suggested training a neural network” - Ariel

“Challenge accepted” - James

import tensorflow as tf
import numpy as np


class IncrementAutoregressive(tf.keras.layers.Layer):
    def __init__(self):
        super().__init__()
        self._inc_weight = tf.Variable(np.random.normal())
        self.terminal_logits_layer = tf.keras.layers.Dense(2)

    def call(self, inputs, **kwargs):
        new_inputs = inputs + self._inc_weight
        terminal_logits = self.terminal_logits_layer(new_inputs)
        return new_inputs, terminal_logits

    def rollout(self, start_val):
        next_num = tf.constant([[start_val]], dtype=tf.float32)
        stop = tf.constant(False)
        result = [next_num]
        while not stop:
            next_num, stop_logits = self(next_num)
            stop = tf.reduce_all(tf.argmax(stop_logits, axis=-1) == 1)
            result.append(next_num)
        return tf.concat(result, axis=0)


def main() -> None:
    layer = IncrementAutoregressive()
    xs = tf.reshape(tf.range(1, 50, dtype=tf.float32), (49, 1))
    ys = xs + 1.0
    stops = tf.concat([tf.fill((48,), 0), [1]], axis=0)
    opt = tf.keras.optimizers.Adam(0.01)
    loss1 = tf.keras.losses.MeanSquaredError()
    loss2 = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

    @tf.function
    def step():
        with tf.GradientTape() as tape:
            pred, pred_stops = layer(xs)
            pred_loss = loss1(ys, pred)
            pred_stops_loss = loss2(stops, pred_stops)
            total_loss = pred_loss + pred_stops_loss
        grad = tape.gradient(total_loss, layer.trainable_weights)
        opt.apply_gradients(zip(grad, layer.trainable_weights))
        return total_loss

    for _ in range(10000):
        loss = step()
        print(loss)

    rollout = layer.rollout(1.0)
    print(tf.round(rollout))


if __name__ == "__main__":
    main()

“Please add TPU support I need this to support big data”.

“100k epochs ensures the right output”.

“When I run it locally I usually get 1-50, and somtimes 1-51. Dat bias term learning is apparently rough”.

“but what if I want to use Reinf Learning, we would surely need a PrintGymEnv”?

OK.

import numpy as np
import gym
from gym.spaces import Box

class PrintEnv(gym.Env):
    def __init__(self, start: int = 1, end: int = 50):
        self.start = start
        self.end = end
        self.counter = start
        
        
        self.observation_space = Box(1, 50, (1,), dtype=np.int32)
        self.action_space = Box(1, 50, (1,), dtype=np.int32)
        
    def reset(self):
        self.counter = self.start
        return self.counter
    
    def step(self, action: int):
        if action == self.counter:
            reward = 1.
            self.counter += 1
        else:
            reward = 0.
            
        if self.counter >= self.end:
            done = True
        else:
            done = False
            
        return self.counter, reward, done, {}
        
    def render(self, mode="human"):
        print(self.counter)

by Ariel.

I think we need a PyTorch version, no?

import torch
from torch import nn, Tensor

class IncrementAutoregressive(nn.Module):
    def __init__(self):
        super().__init__()
        self.inc_weight = nn.Parameter(torch.normal(0., 1., (1,), requires_grad=True))
        self.terminal_logits_layer = nn.Linear(1, 2)

    def forward(self, inputs):
        new_inputs = inputs + self.inc_weight
        terminal_logits = self.terminal_logits_layer(new_inputs)
        return new_inputs, terminal_logits

def rollout(model: IncrementAutoregressive, start_val: int) -> Tensor:
    next_num = torch.tensor([start_val])
    stop = False
    result = [next_num]
    i = 0
    while not stop:
        next_num, stop_logits = layer(next_num)
        stop = stop_logits.argmax().item()
        result.append(next_num.detach())
        i += 1

    return torch.cat(result).round()

def train(layer: nn.Module):

    xs = torch.arange(1, 50).view((49, 1)).to(torch.float32)
    ys = xs + 1.0
    stops = torch.zeros_like(xs).view((49,)).to(torch.long)
    stops[-1] = 1.

    opt = torch.optim.Adam(layer.parameters(), 0.05)

    val_loss = nn.MSELoss()
    stop_loss = nn.CrossEntropyLoss()

    for t in range(10000):
        pred, pred_stops = layer(xs)

        loss = val_loss(pred, ys) + stop_loss(pred_stops, stops)

        opt.zero_grad()
        loss.backward()
        opt.step()

        if t % 1000 == 0:
            print(f"Loss at step {t}: {loss.item()}")

layer = IncrementAutoregressive()
train(layer)
result = rollout(layer, 1.)
print(result)

also by Ariel

State-of-the-art algorithm to print from 1 to 50

The council consists of Ariel and me.

Another solution, inspired by the State-Of-The-Art sorting algorithm SleepSort, using advanced concurrent programming techniques:
from threading import Thread
import time

def wait_and_print(n: int):
    time.sleep(n)
    print(n)

threads = [Thread(target=wait_and_print, args=(i,)) for i in range(1, 50)]

for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

“Let’s be language agnostic” - OrganicPitaChips:

>+++++++++++[-<+++++>] # initialize 55??? at first cell
>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-
<+>]>+>>]<<<<<]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++
<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]

Final take

Thanks everyone who participated and enjoyed in this conversation!