Max Kaye's Site

Max Kaye's Site


Max's Microblog

Self-hosted replacement for

climbing update (28 aug)

I started doing outdoor bouldering about a week ago (we've been in lockdown for the last ~2 months). I found an unclimbed wall that's close enough to home (for current exercise-distance restrictions) and have been working on that recently. I've posted some videos of this to my max's bouldering playlist on YT. I've also documented the wall on theCrag:

Before lockdown I was 4-5 weeks in to my 'send all the purples' goal at my gym (purples are bouldering routes of a particular grade) -- my gym resets a few sections of wall every week and has a 6 week cycle.

Optimal encoding of permutations

This is a repost of a post I made to reddit on June 13th 2017. I found it again recently and wanted to document it here.

The Problem (what's the optimal encoding?)

You have a known list of n candidates on a ballot. In the act of voting, the voter must number each candidate from 1 to n, using each number once. The voter is thus describing a transformation on the list of candidates into a particular permutation. What is the minimum number of bits needed to exactly describe any order, and the method?

My naive approach is to count (from the left) the number of 'jumps' each candidate must take to arrive in their ranked position skipping any previously ranked candidates.

So if we had 3 candidates: cs = ['c1', 'c2', 'c3'] and a list of ranks rs = [2, 3, 1] we'd do this (where zip(cs, rs) matches candidates to ranks):

  • First, c1 should be in the 2nd position, which is 1 move from the far left open spot. Since c1 could move 2 positions though we need to use 2 bits. Record 01
  • Second, 'c2' needs to be in 3rd position, which is also 1 move from the far left open spot (since we skip over the filled position from step 1). Because 'c2' could only move a maximum of 1 jump, we only need 1 bit: record 1.
  • Third, 'c3' trivially fits into the last space, so we need 0 bits.

Our final result is 011.

To decode the permutation, take N bits in the sequence ceil(log(2, n)), ceil(log(2, n-1)), ceil(log(2, n-2)), ..., ceil(log(2, 2)), which is [2, 1, 0] in our case. Use those bits (in this case ['01', '1']) to move each candidate into open spots.

By numerical analysis this method very closely fits O(n Log n) space complexity. (total_bits = 0.89 * n * log(2, n) seems to describe the pattern very closely).

The exact number of bits used will be t = Sigma(i=2, n)( ceil(log(2, i)) ).

It occurs to me that simply listing the positions is O(n log n) too, but the exact number of bits required is t = ceil(log(2, n))*n which is more than my method.

Reddit user tilrman pointed out:

With n candidates, there are P(n) = n! permutations. Each voter selects one of these permutations, so a vote can be represented by an integer [0, n!-1].

2 more comments follow in that branch before I posted my soln

My Solution

clarification: in "base factorial" I'm using base in the same way as base 10 (our number system) or base 2 (binary numbers).

Basic idea is to use factoradics - sort of like base factorial where the kth integer is in [0, k] and to convert back to base 10 you'd multiply it by k!. e.g.: 3210 is 0*1! + 1*2! + 2*3! + 3*4!. Also 4321 + 1 = 10000 which is 1*5! = 120 in base 10.

These numbers uniquely enumerate permutations for a list of n unique elements and allow one to 'read' the permutation from the number. e.g. [a, b, c, d, e] in permutation 2310 leads to [d, c, a, e, b]. Put a in 2+1th free slot, put b in 3+1th free spot, put c in 1+1th free slot, put d in 0+1th free slot, put e in the last spot.

This means we can map all integers in [0, n!-1] to a unique permutation (via modulo-ing and subtracting successive factorials), and back again quickly and efficiently (though arbitrarily sized integers (e.g. in python or haskell) make this much easier than small integers - 150! is like 110 bytes long as a raw integer)

Finally a sanity check: for a list of k elements we have a maximum count of (k-1) (k-2) ... (k-k+1) where each bracketed expression is a digit. If we add 1 we end up with 1 followed by k zeros, which corresponds to k!, and so our maximum integer is 1 less at k!-1 which is what we expect :)

Louis Rossmann Vids

I like a lot of Louis Rossmann's videos and his thoughts.

This topic is for discussing and collecting good ones. Here's two; one recent, one old

*Freedom* and *Universality* are linked

I think freedom and universality must have some deep connection.

If you have the freedom to do something, that means -- wrt that context -- you can do whatever you like. You're unconstrained; that aspect cannot (or at least should not) be a bottleneck.

That gives you some unboundedness wrt method, or content, or whatever the freedom relates to. Freedom of speech removes a bound on speech. Freedom of thought removes a bound on thought.

Freedom enables universality; and without certain freedoms, you cannot have certain universalities.

I thought that freedom and thinking were like orthogonal; both necessary for philosophy and good life, but neither sufficient. Now I suspect there's more there than I realized.

climbing goals - gyms reopen

Climbing gyms (and everything else) have just reopened (for the vaccinated) in Sydney, following a ~3.5 month lockdown. Now that climbing is frequent and regular again, I've been reconsidering climbing goals.

Before lockdown, my goal was to send every new grade 31 (out of 6) problem, every week, for 6 weeks (I'd done 5 weeks when lockdown started). 6 weeks is a full rotation, so achieving this means that I would have consistently been able to project any purple in 2-3 sessions. The purpose of this goal was to emphasise consistency over results (better to do be capable of any grade 3 problem than some grade 3 and some grade 4).

The current goal I've settled on is to send every grade 3 climb (in the gym) in a week -- there's about 24 or so (give or take depending on what gets set). I sent 10 this morning (over about 2hrs). I don't think I'd have been able to do that if I hadn't done some outdoor climbing during lockdown, tho. So far, so good. I have been going through them methodically, with a few exceptions (picking ones I thought I could do towards the end of the session, for example). So far no grade 3 has been particularly challenging, though crowd-beta has helped a bit. Major bottleneck atm (session-to-session) is probably endurance -- going to monitor that and take corrective action if it doesn't improve this week.

  1. the problems are color-graded, so grade 3 of 6 is purple, and 4 of 6 is black. there's no precise conversion to V-grades, but I guess purple is V2-5 and black is V4-? (6 or 7, maybe). 


Correction: I sent 13 / 25 purples (grade 3) on Monday.

I sent 9 of the remaining 12 this morning. so only 3 left for Friday morning -- lots of buffer!

I also sent my first grade 5 (yellow)!


I sent just 1 of the remaining 3 purples on Friday.

The current goal I've settled on is to send every grade 3 climb (in the gym) in a week

WRT this goal, I could go again this weekend, but I think it'd be better to rest. That means I'll fail the goal. What happens wrt the goal then?

The purpose of the goal was to build consistency and mastery of a particular grade. Or maybe, that's what the goal aligned with -- it wasn't, in and of itself, to master the grade. Rather, it was a breakpoint that I chose which aligned with master. If I didn't meet it, then it's a criticism of the idea that I've mastered that grade. Mastering a grade isn't a quality I'd lose unless I stopped climbing, though (or had some injury or something). So what does failing this goal really mean?

Well, I'm not yet inconsistent when it comes to purples (since I had been sending them all prior to lockdown) -- so more time's needed to determine that. It is a criticism of mastery though; it means there are still bottlenecks at this grade.

The reason I chose the original parameters that I did -- 1 week to send every new purple for a full cycle -- was that 1 week allowed for reasonable buffer, and a full cycle meant ~25 new climbs. New routes are set Tuesday-Thursday, so 4 days of the week were the period of stability before the next wave. Once those new climbs were set, a failure to send one that was taken down is a decisive criticism of both consistency and mastery. Also, sending all the new routes (before the next wave was set) meant that I'd have excess capacity to try new climbs, or work on bottlenecks.

Even without sending every purple, I still have excess capacity -- different climbs use different resources. And since I shouldn't lose the ability to send-every-purple if I keep improving, why would I ever stop?

I think mb goals like this are sometimes motivating (they have been to me) but there's a problem if you get invested in them. That's because failing a goal is, like, something to take personally. But that isn't what these sort of goals are really about. A goal like this is a regular opportunity to take a benchmark -- and mb find a weakness in your abilities that you didn't know was there. Knowing about that is a good thing! If you don't know it's there (or if you know and ignore it) then it becomes a thing you can't rely on. That's a problem if it ever needs to be foundational to other, more advanced knowledge. (Like, it's a dependency.)

This is consistent (and I think related) with a different idea: achievement of goals is pointless if you're not challenged. There's no point in me setting a goal if I know it's already too easy. The achievement is hollow.

Moreover, if there are always meaningful goals to choose -- meaningful problems to solve -- then choosing goals that are foregone conclusions must be meaningless. My guess is that -- for some projects -- it is moral to choose meaningful goals, and immoral to deliberately choose meaningless goals when meaningful ones could be chosen instead. (Sometimes those projects/decisions only impact a single person, in which case they're probably amoral -- that's okay. Morality is about interpersonal harm, and people are free to live their own lives (wrt themselves) how they wish. People are also under no obligation to maximize morality -- just to not act immorally -- so provided there's no malice, most goals are okay.)

Getting back to purples: I think new climbs is an important breakpoint, so I'm going to monitor that going forward. I'm also thinking I'll adopt the send-every-purple-every-week goal again, indefinitely -- it's a useful warning sign of bottlenecks, and means there's some variance in my excess capacity (which I can use to my advantage). Maybe a good breakpoint to re-evaluate at is some level of consistency with blacks, like sending >50% each week. That seems reasonable atm.

Addendum by u/max after 2021-10-23 03:59:29 UTC 7 days
I was so very wrong about morality and interpersonal harm.

I've read Galt's speech.

In n/10017 (the parent of this post) I said:

Sometimes those projects/decisions only impact a single person, in which case they're probably amoral -- that's okay. Morality is about interpersonal harm, [...].

I was so very wrong about this. Morality does concern one's actions, since some actions are right and some are wrong. But morality is not about harm (interpersonal or otherwise), at least, no more so than it is about rocks.

I am starting to understand.

I'm not sure enough, yet, to say what morality is about -- I could try, but I don't want to rush it. I want know it before I claim to know it. Soon, I will be sure enough.

I'm grateful that JustinCEO (on the CF forum) noticed this part of n/10017 and challenged me on it, and I'm grateful that he and ingracke found it worth their time to discuss it with me.

I am grateful for Ayn Rand -- her life, her works, and most of all: her mind. Right now, I'm grateful particularly for Atlas Shrugged.

I have a special gratitude for Elliot, not only for his participation in the above-linked thread (among many other things, too many to list here), but also -- and, right now, primarily -- for his labor, dedication and ideas that safeguard the closest thing I've ever known to a sanctuary.

Why do I express my gratitude? I admire their virtues. Why am I grateful? Their virtues have allowed me to profit, and I will continue to. And I know that they have and will, too.


Of those two problem purples, I sent one on Monday evening, and one on Thursday evening. There were 6 new purples this week (3x set on Tuesday, and 3x on Thursday). On Wednesday morning I sent the 3x new purples that were set on Tuesday, and on Thursday evening I sent the 3x purples that were set that day (and 2x of the blacks that were set that day, too).

I made a video of 43 attempts at a particular boulder, played over the top of eachother at the same time.


Source code:

The method is documented in the repo readme.

`giphy-dl` script (in the style of youtube-dl for giphy)

make sure to chmod +x it and should just work (provided you have curl and extended grep support)

#!/usr/bin/env bash


if [ -z "$FILENAME" ] || [ -z "$2" ]; then
  echo "usage: $0 GIF_NAME GIPHY_URL_OR_CODE"
  exit 1

CODE_FROM_URL=$(echo "$GIPHY_CODE_OR_URL" | egrep -o 'media\/([A-Za-z0-9]{12,})\/giphy' | cut -d '/' -f 2)

if [ -z "$CODE_FROM_URL" ]; then
  CODE_FROM_URL=$(echo "$GIPHY_CODE_OR_URL" | egrep -o '[A-Za-z0-9]{12,}')

echo "saving to: $FILENAME.mp4"
echo "dling gif from: $GIPHY_CODE_OR_URL"
echo "giphy code: $CODE_FROM_URL"

curl "$CODE_FROM_URL/giphy.mp4" --output "$FILENAME.mp4"

xdg-open "$FILENAME.mp4" || true
self limiting lets you maintain excap

e.g., governor

  • relevant to learning
  • redlining in a controlled env can be useful for like measuring progress or something, but that shouldn't be the norm
  • by maintaining excap one can ensure there's room to move -- buffer. that's important while learning b/c you're focusing on creating new organization of knowledge and reorganizing existing knowledge. trying to learn without excap is like trying to do tetris with no v-space. with lots of v-space tetris is manageable, but it gets hyperbolically harder as you approach the top.

re long-running learning conflict: the more exploratory side means exhausting excap quickly (manageable when surveying, but not when trying to complete a project).

ffmpeg script: mod-video-speed

this script will speed up (or slow down) video and audio via ffmpeg.

put the following in e.g., ~/.local/bin/mod-video-speed and chmod +x it.

#!/usr/bin/env python3

import subprocess
import sys, re

this_file = sys.argv[0]
filename = this_file.split('/')[-1]
args = sys.argv[1:]

if len(args) != 3:
  print(f'Error: need 3 args but you provided {len(args)}. {args}')
  print(f'\n\tUSAGE: {filename} INFILE OUTFILE SPEED')
  print(f'\n\tINFILE: input file (source)\n\tOUTFILE: output file (destination)')
  print(f'\tSPEED: the speed of the output video in the format `2x` or `1.43x` -- must end with `x`')

in_file = sys.argv[1]
out_file = sys.argv[2]
speed_raw = sys.argv[3]
speed_pattern = r'([0-9]+\.?[0-9]*)x'
speed_re = re.match(speed_pattern, speed_raw)
if speed_re is None:
  print(f"Error: SPEED argument did not match `{speed_pattern}`\n\nExamples: `1x`, `0.5x`, `123.456x`, `99x`")
speed = float(speed_re[1])
pts_speed = 1./speed
print(f"Processing {in_file} and modifying video speed to {speed}, outputting to {out_file}.")

ffmpeg_cmd_list = ['ffmpeg', '-i', in_file, '-filter_complex',
                   '-map', '[v]', '-map', '[a]', out_file]
print(f"Running ffmpeg cmd: {' '.join(ffmpeg_cmd_list)}")

license: public domain

sydney boulder series 2021 results archive

archiving for my own future purposes + i don't know if this is replicated anywhere else on the web.

  • round 1:
  • round 2:
  • round 3:
  • round 4:
  • combined 1,2,3,4:
  • combined 1,2,3:
  • participant info pack: