Twelve Clock List

Documenting the options for a clock game

…What?

Whenever I see a digital clock, I do a little math in my head. I try to take the numbers and, using only addition, subtraction, multiplication and division, get them to come out to twelve. I thought I’d try to get an exhaustive list of times that this trick applies to. Let’s figure it out!

import re
import pandas as pd

import seaborn as sns
import matplotlib.pyplot as plt

Get with the times

First we need to get a list of the times. I’m going to work with strings. Let’s go through some numbers and rule out invalid ones by places.

valid = []
for x in range(100, 1260):
    num = ''
    num += str(x)
    
    if len(num) == 3:
        if int(num[0]) <= 9 and int(num[1]) <= 5:
            valid.append(num)
    if len(num) == 4:
        if int(num[0]) <= 1 and int(num[1]) <= 2 and int(num[2]) <= 5:
            valid.append(num)

Cool. We can check that we have 720, which is half of all times since they all repeat once. Now we can go about making possible math expressions.

len(valid)
720
valid[:4]
['100', '101', '102', '103']

Prep the times

To do this, I’m going to put together strings of math expressions for all permutations of the numbers, then evaluate them and see which ones work. Certainly brute force, but it’ll work. First, I make a dictionary of all permutations of the numbers for each time.

# moded from https://www.geeksforgeeks.org/python-permutation-given-string-using-inbuilt-function/
from itertools import permutations 

def allPermutations(str): 
    
    perm_array = []

    # Get all permutations of string 'ABC' 
    permList = permutations(str) 

    # print all permutations 
    for perm in list(permList): 
        result = ''.join(perm)
        perm_array.append(result)
    
    return list(set(perm_array))
        
time_dict = {}

for time in valid:
    time_dict[time] = allPermutations(time)
time_dict['957']
['795', '957', '579', '597', '759', '975']

Now, I go and add in all possible combinations of operators (+, -, *, /) in between the numbers. I also add in parens since eval follows PEMDAS and we want to avoid that in many cases.

equations = {}

for time, perms in time_dict.items():
    eq = ''
    ops = '+-*/'
    if len(time) == 3:
        equations[time] = []
        for perm in perms:
            for a in ops:
                for b in ops:
                    eq = f"{perm[0]}{a}{perm[1]}{b}{perm[2]}"
                    eq = re.sub(r'([0-9])', r'\1)', eq)
                    eq = '(((' + eq
                    equations[time].append(eq)
    if len(time) == 4:
        equations[time] = []
        for perm in perms:
            for a in ops:
                for b in ops:
                    for c in ops:
                        eq = f"{perm[0]}{a}{perm[1]}{b}{perm[2]}{c}{perm[3]}"
                        eq = re.sub(r'([0-9])', r'\1)', eq)
                        eq = '((((' + eq
                        equations[time].append(eq)
                            
equations['100'][:10]
['(((0)+0)+1)',
 '(((0)+0)-1)',
 '(((0)+0)*1)',
 '(((0)+0)/1)',
 '(((0)-0)+1)',
 '(((0)-0)-1)',
 '(((0)-0)*1)',
 '(((0)-0)/1)',
 '(((0)*0)+1)',
 '(((0)*0)-1)']

Look at the time

Cool. Now that we have a bunch of expressions, let’s evaluate them and see what we get.

they_work = {}

for time, eq_list in equations.items():
    for eq in eq_list:
        try:
            result = eval(eq)
        except ZeroDivisionError:
            continue
        if result == 12 and time not in they_work.keys():
            they_work[time] = eq
they_work
{'116': '(((1)+1)*6)',
 '124': '(((2)+1)*4)',
 '125': '(((5)+1)*2)',
 '126': '(((6)*1)*2)',
 '127': '(((7)-1)*2)',
 '129': '(((9)+1)+2)',
 '133': '(((1)+3)*3)',
 '134': '(((1)*4)*3)',
 '135': '(((5)-1)*3)',
 '136': '(((3)-1)*6)',
 '138': '(((1)+3)+8)',
 '139': '(((3)+9)*1)',
 '142': '(((2)+1)*4)',
 '143': '(((1)*4)*3)',
 '144': '(((4)-1)*4)',
 '147': '(((7)+4)+1)',
 '148': '(((8)+4)*1)',
 '149': '(((9)+4)-1)',
 '152': '(((5)+1)*2)',
 '153': '(((5)-1)*3)',
 '156': '(((6)+1)+5)',
 '157': '(((1)*7)+5)',
 '158': '(((5)-1)+8)',
 '206': '(((2)+0)*6)',
 '214': '(((2)+1)*4)',
 '215': '(((5)+1)*2)',
 '216': '(((6)*1)*2)',
 '217': '(((7)-1)*2)',
 '219': '(((9)+1)+2)',
 '223': '(((3)*2)*2)',
 '224': '(((2)+4)*2)',
 '225': '(((5)*2)+2)',
 '227': '(((7)*2)-2)',
 '228': '(((2)+2)+8)',
 '232': '(((3)*2)*2)',
 '233': '(((3)+3)*2)',
 '236': '(((2)*3)+6)',
 '237': '(((2)+3)+7)',
 '238': '(((3)/2)*8)',
 '239': '(((9)-3)*2)',
 '241': '(((2)+1)*4)',
 '242': '(((2)+4)*2)',
 '244': '(((4)*2)+4)',
 '245': '(((5)-2)*4)',
 '246': '(((4)+6)+2)',
 '248': '(((2)*8)-4)',
 '251': '(((5)+1)*2)',
 '252': '(((5)*2)+2)',
 '254': '(((5)-2)*4)',
 '255': '(((2)+5)+5)',
 '259': '(((5)-2)+9)',
 '304': '(((0)+4)*3)',
 '309': '(((0)+3)+9)',
 '313': '(((1)+3)*3)',
 '314': '(((1)*4)*3)',
 '315': '(((5)-1)*3)',
 '316': '(((3)-1)*6)',
 '318': '(((1)+3)+8)',
 '319': '(((3)+9)*1)',
 '322': '(((2)+2)*3)',
 '323': '(((3)+3)*2)',
 '326': '(((2)*3)+6)',
 '327': '(((2)+3)+7)',
 '328': '(((3)/2)*8)',
 '329': '(((9)-3)*2)',
 '331': '(((1)+3)*3)',
 '332': '(((3)+3)*2)',
 '333': '(((3)*3)+3)',
 '335': '(((5)*3)-3)',
 '336': '(((3)+6)+3)',
 '337': '(((7)-3)*3)',
 '340': '(((0)+4)*3)',
 '341': '(((1)*4)*3)',
 '345': '(((5)+3)+4)',
 '346': '(((6)-3)*4)',
 '348': '(((8)-4)*3)',
 '349': '(((9)/3)*4)',
 '351': '(((5)-1)*3)',
 '353': '(((5)*3)-3)',
 '354': '(((5)+3)+4)',
 '356': '(((5)-3)*6)',
 '359': '(((9)-5)*3)',
 '403': '(((0)+4)*3)',
 '408': '(((8)+4)+0)',
 '412': '(((2)+1)*4)',
 '413': '(((1)*4)*3)',
 '414': '(((4)-1)*4)',
 '417': '(((7)+4)+1)',
 '418': '(((8)+4)*1)',
 '419': '(((9)+4)-1)',
 '421': '(((2)+1)*4)',
 '422': '(((2)+4)*2)',
 '424': '(((4)*2)+4)',
 '425': '(((5)-2)*4)',
 '426': '(((2)+6)+4)',
 '428': '(((2)*8)-4)',
 '430': '(((0)+4)*3)',
 '431': '(((1)*4)*3)',
 '435': '(((5)+3)+4)',
 '436': '(((6)-3)*4)',
 '438': '(((8)-4)*3)',
 '439': '(((9)/3)*4)',
 '441': '(((4)-1)*4)',
 '442': '(((2)*4)+4)',
 '444': '(((4)+4)+4)',
 '447': '(((7)-4)*4)',
 '452': '(((5)-2)*4)',
 '453': '(((5)+3)+4)',
 '458': '(((8)-5)*4)',
 '507': '(((5)+7)+0)',
 '512': '(((5)+1)*2)',
 '513': '(((5)-1)*3)',
 '516': '(((5)+1)+6)',
 '517': '(((1)*7)+5)',
 '518': '(((5)-1)+8)',
 '521': '(((5)+1)*2)',
 '522': '(((5)*2)+2)',
 '524': '(((5)-2)*4)',
 '525': '(((2)+5)+5)',
 '529': '(((5)-2)+9)',
 '531': '(((5)-1)*3)',
 '533': '(((5)*3)-3)',
 '534': '(((5)+3)+4)',
 '536': '(((5)-3)*6)',
 '539': '(((9)-5)*3)',
 '542': '(((5)-2)*4)',
 '543': '(((5)+3)+4)',
 '548': '(((8)-5)*4)',
 '552': '(((2)+5)+5)',
 '602': '(((2)+0)*6)',
 '606': '(((6)+6)+0)',
 '611': '(((1)+1)*6)',
 '612': '(((6)*1)*2)',
 '613': '(((3)-1)*6)',
 '615': '(((6)+1)+5)',
 '616': '(((1)*6)+6)',
 '617': '(((6)+7)-1)',
 '620': '(((2)+0)*6)',
 '621': '(((6)*1)*2)',
 '623': '(((2)*3)+6)',
 '624': '(((4)+6)+2)',
 '628': '(((6)+8)-2)',
 '629': '(((9)*2)-6)',
 '631': '(((3)-1)*6)',
 '632': '(((2)*3)+6)',
 '633': '(((3)+6)+3)',
 '634': '(((6)-3)*4)',
 '635': '(((5)-3)*6)',
 '636': '(((3)*6)-6)',
 '639': '(((9)-3)+6)',
 '642': '(((2)+6)+4)',
 '643': '(((6)-3)*4)',
 '646': '(((6)-4)*6)',
 '648': '(((6)*8)/4)',
 '649': '(((9)-6)*4)',
 '651': '(((6)+1)+5)',
 '653': '(((5)-3)*6)',
 '657': '(((7)-5)*6)',
 '705': '(((0)+5)+7)',
 '712': '(((7)-1)*2)',
 '714': '(((7)+4)+1)',
 '715': '(((1)*7)+5)',
 '716': '(((6)+7)-1)',
 '721': '(((7)-1)*2)',
 '722': '(((2)*7)-2)',
 '723': '(((2)+3)+7)',
 '727': '(((7)+7)-2)',
 '732': '(((2)+3)+7)',
 '733': '(((7)-3)*3)',
 '738': '(((7)-3)+8)',
 '739': '(((7)*3)-9)',
 '741': '(((7)+4)+1)',
 '744': '(((7)-4)*4)',
 '749': '(((9)-4)+7)',
 '750': '(((0)+5)+7)',
 '751': '(((1)*7)+5)',
 '756': '(((7)-5)*6)',
 '804': '(((8)+4)+0)',
 '813': '(((1)+3)+8)',
 '814': '(((8)+4)*1)',
 '815': '(((5)-1)+8)',
 '822': '(((8)+2)+2)',
 '823': '(((3)/2)*8)',
 '824': '(((2)*8)-4)',
 '826': '(((6)+8)-2)',
 '828': '(((8)/2)+8)',
 '831': '(((1)+3)+8)',
 '832': '(((3)/2)*8)',
 '834': '(((8)-4)*3)',
 '837': '(((7)-3)+8)',
 '840': '(((8)+4)+0)',
 '841': '(((8)+4)*1)',
 '842': '(((2)*8)-4)',
 '843': '(((8)-4)*3)',
 '845': '(((8)-5)*4)',
 '846': '(((6)*8)/4)',
 '848': '(((8)-4)+8)',
 '851': '(((5)-1)+8)',
 '854': '(((8)-5)*4)',
 '859': '(((9)+8)-5)',
 '903': '(((0)+3)+9)',
 '912': '(((9)+1)+2)',
 '913': '(((3)+9)*1)',
 '914': '(((9)+4)-1)',
 '921': '(((9)+1)+2)',
 '923': '(((9)-3)*2)',
 '925': '(((5)-2)+9)',
 '926': '(((9)*2)-6)',
 '930': '(((0)+3)+9)',
 '931': '(((3)+9)*1)',
 '932': '(((9)-3)*2)',
 '934': '(((9)/3)*4)',
 '935': '(((9)-5)*3)',
 '936': '(((9)-3)+6)',
 '937': '(((7)*3)-9)',
 '939': '(((9)/3)+9)',
 '941': '(((9)+4)-1)',
 '943': '(((9)/3)*4)',
 '946': '(((9)-6)*4)',
 '947': '(((9)-4)+7)',
 '952': '(((5)-2)+9)',
 '953': '(((9)-5)*3)',
 '958': '(((9)+8)-5)',
 '1016': '((((1)+1)+0)*6)',
 '1024': '((((1)+2)+0)*4)',
 '1025': '((((5)+0)+1)*2)',
 '1026': '((((1)*2)*6)+0)',
 '1027': '((((7)+0)-1)*2)',
 '1029': '((((1)+9)+0)+2)',
 '1033': '((((3)+1)+0)*3)',
 '1034': '((((1)*3)*4)+0)',
 '1035': '((((5)+0)-1)*3)',
 '1036': '((((3)-1)+0)*6)',
 '1038': '((((1)+8)+0)+3)',
 '1039': '((((0)+9)*1)+3)',
 '1042': '((((1)+2)+0)*4)',
 '1043': '((((1)*3)*4)+0)',
 '1044': '((((4)-1)+0)*4)',
 '1047': '((((0)+1)+4)+7)',
 '1048': '((((8)*1)+4)+0)',
 '1049': '((((4)+0)+9)-1)',
 '1052': '((((5)+0)+1)*2)',
 '1053': '((((5)+0)-1)*3)',
 '1056': '((((6)+0)+5)+1)',
 '1057': '((((7)+0)+5)*1)',
 '1058': '((((8)+0)+5)-1)',
 '1106': '((((1)+1)+0)*6)',
 '1114': '((((1)+1)+1)*4)',
 '1116': '((((1)+1)*6)*1)',
 '1119': '((((9)+1)+1)+1)',
 '1123': '((((2)+1)+1)*3)',
 '1124': '((((4)+1)+1)*2)',
 '1125': '((((2)*5)+1)+1)',
 '1126': '((((2)*1)*6)*1)',
 '1127': '((((7)-1)*2)*1)',
 '1128': '((((2)+1)+1)+8)',
 '1129': '((((1)+2)*1)+9)',
 '1132': '((((2)+1)+1)*3)',
 '1133': '((((1)*1)+3)*3)',
 '1134': '((((3)*1)*4)*1)',
 '1135': '((((5)-1)*3)*1)',
 '1136': '((((1)*3)-1)*6)',
 '1137': '((((3)+1)+1)+7)',
 '1138': '((((3)+1)+8)*1)',
 '1139': '((((9)+3)+1)-1)',
 '1141': '((((1)+1)+1)*4)',
 '1142': '((((4)+1)+1)*2)',
 '1143': '((((3)*1)*4)*1)',
 '1144': '((((4)-1)*4)*1)',
 '1145': '((((5)-1)-1)*4)',
 '1146': '((((1)+4)+1)+6)',
 '1147': '((((1)+7)+4)*1)',
 '1148': '((((1)+8)+4)-1)',
 '1149': '((((4)+9)-1)*1)',
 '1152': '((((2)*5)+1)+1)',
 '1153': '((((5)-1)*3)*1)',
 '1154': '((((5)-1)-1)*4)',
 '1155': '((((5)+1)+5)+1)',
 '1156': '((((1)+6)+5)*1)',
 '1157': '((((7)+1)+5)-1)',
 '1158': '((((5)-1)+8)*1)',
 '1159': '((((5)-1)+9)-1)',
 '1204': '((((1)+2)+0)*4)',
 '1205': '((((5)+0)+1)*2)',
 '1206': '((((1)*2)*6)+0)',
 '1207': '((((7)+0)-1)*2)',
 '1209': '((((1)+9)+0)+2)',
 '1213': '((((2)+1)+1)*3)',
 '1214': '((((4)+1)+1)*2)',
 '1215': '((((2)*5)+1)+1)',
 '1216': '((((2)*1)*6)*1)',
 '1217': '((((7)-1)*2)*1)',
 '1218': '((((2)+1)+1)+8)',
 '1219': '((((1)+2)*1)+9)',
 '1222': '((((1)+2)*2)*2)',
 '1223': '((((2)+2)*3)*1)',
 '1224': '((((1)+4)*2)+2)',
 '1225': '((((2)+5)-1)*2)',
 '1226': '((((2)/2)+1)*6)',
 '1227': '((((1)+2)+7)+2)',
 '1228': '((((2)+1)*8)/2)',
 '1229': '((((2)+9)-1)+2)',
 '1231': '((((1)+1)*3)*2)',
 '1232': '((((2)+2)*3)*1)',
 '1233': '((((3)-1)*3)*2)',
 '1234': '((((3)+1)*2)+4)',
 '1235': '((((2)*5)+3)-1)',
 '1236': '((((6)+3)+2)+1)',
 '1237': '((((1)*3)+2)+7)',
 '1238': '((((2)+3)-1)+8)',
 '1239': '((((9)-1)*3)/2)',
 '1240': '((((1)+2)+0)*4)',
 '1241': '((((4)+1)+1)*2)',
 '1242': '((((1)+4)*2)+2)',
 '1243': '((((3)+1)*2)+4)',
 '1244': '((((2)*4)+4)*1)',
 '1245': '((((5)+1)+4)+2)',
 '1246': '((((1)*6)+2)+4)',
 '1247': '((((4)+2)+7)-1)',
 '1248': '((((8)*1)*2)-4)',
 '1249': '((((4)-2)+9)+1)',
 '1250': '((((5)+0)+1)*2)',
 '1251': '((((2)*5)+1)+1)',
 '1252': '((((2)+5)-1)*2)',
 '1253': '((((2)*5)+3)-1)',
 '1254': '((((5)+1)+4)+2)',
 '1255': '((((1)*5)+2)+5)',
 '1256': '((((5)+2)+6)-1)',
 '1257': '((((2)-1)*7)+5)',
 '1258': '((((8)+1)-2)+5)',
 '1259': '((((2)*9)-5)-1)'}
print(f"So this trick works {len(they_work.keys()) / len(valid) * 100:.2f}% of the time")
So this trick works 45.97% of the time

Well…there ya go

There are a bunch that are easy to spot. Was interesting to see ones where division is used.

[x for x in they_work.values() if '/' in x]
['(((3)/2)*8)',
 '(((3)/2)*8)',
 '(((9)/3)*4)',
 '(((9)/3)*4)',
 '(((6)*8)/4)',
 '(((3)/2)*8)',
 '(((8)/2)+8)',
 '(((3)/2)*8)',
 '(((6)*8)/4)',
 '(((9)/3)*4)',
 '(((9)/3)+9)',
 '(((9)/3)*4)',
 '((((2)/2)+1)*6)',
 '((((2)+1)*8)/2)',
 '((((9)-1)*3)/2)']

Maybe you’ll give it a try next time you spot a digital clock…

DataFrame of the results

df = pd.DataFrame(valid)
df.rename(columns={0: 'raw'}, inplace=True)
df['raw_temp'] = df['raw'].apply(lambda x: '0' + x if len(x) == 3 else x)
df['time'] = df['raw_temp'].apply(lambda x: pd.to_datetime(x, format='%H%M'))
df.head()

raw raw_temp time twelve
0 100 0100 1900-01-01 01:00:00 False
1 101 0101 1900-01-01 01:01:00 False
2 102 0102 1900-01-01 01:02:00 False
3 103 0103 1900-01-01 01:03:00 False
4 104 0104 1900-01-01 01:04:00 False
valid_times = list(they_work.keys())
df['twelve'] = df['raw'].isin(valid_times)
df.head()

raw raw_temp time twelve
0 100 0100 1900-01-01 01:00:00 False
1 101 0101 1900-01-01 01:01:00 False
2 102 0102 1900-01-01 01:02:00 False
3 103 0103 1900-01-01 01:03:00 False
4 104 0104 1900-01-01 01:04:00 False

Image Credit

Twelve by Zach Bogart from the Noun Project

Zach Bogart
Zach Bogart
Data Explorer

Science, Design, & Data