ANAGRANAMES ← Search

How it works

Cool tricks

This project uses many nice algorithms and technologies. The rest of the page explains them in detail; here are my favorites.

The full code is on GitHub, with the MIT license.

What data do you use?

Cleaning: we keep a person only if the two names are in Latin script, with letters, spaces, hyphens, apostrophes and periods only. Then we remove the names that fewer than 5 people have in the world: most are typing errors or fake names. This keeps 352,931,936 people, 1,724,039 first names, 3,456,059 last names and 154,775,215 different full names. From INSEE, we remove the lines that put the rare names together.

How do you find anagranames?

A name becomes its letters A to Z, without accents, spaces or hyphens: Jean-Paul becomes JEANPAUL. Two full names are anagrams when they have the same letters.

  1. We sort the letters of each name, the rarest letters (Q, X, Z…) first. The first names go in a trie of these sorted letters: a tree where each node adds one letter, and each name is at the node of its last letter. The last names go in a second trie.
  2. The search goes down the trie of the first names. It enters a node only if your name has one more of its letter, and if 2 letters at least stay for the last name.
  3. At each first name, the letters that stay give one path in the trie of the last names. All the last names at the end of this path make anagranames with this first name.

The rare letters at the top cut most branches early: a search takes a few milliseconds. In a country, the search keeps the names seen in this country. The list of the real anagranames is in order of accounts. The list of the synthetic anagranames is in order of probability.

How do you rank the synthetic anagranames?

A model gives the probability that a person of the country $c$ has the first name $f$ and the last name $l$. It starts from the two names alone:

$$p_0(f,l\mid c)=p(f\mid c)\,p(l\mid c),\qquad p(f\mid c)=\frac{N_{fc}+\alpha\,\pi_f}{N_c+\alpha}$$

with $N_{fc}$ the people of $c$ with the first name $f$, $N_c$ the people of $c$, $\pi_f$ the share of $f$ in the world and $\alpha=10^5$ (the same for $p(l\mid c)$). For France, $N_{fc}$ is the people with $f$ in INSEE, scaled to the people of France in name_dataset. Then the model corrects the pair. Each name has a vector of 16 numbers: $u_f$ for a first name, $v_l$ for a last name. Each country has 16 weights $g_c$, a scale $\beta_c$ and an offset $b_c$:

$$\log p(f,l\mid c)=\log p_0(f,l\mid c)+\mathbb 1[(f,l)\in S_c]\,s_c(f,l)-\log Z_c$$ $$s_c(f,l)=\beta_c\,u_f^\top\operatorname{diag}(g_c)\,v_l+b_c$$

$S_c$ contains the pairs whose two names occur in $c$ (for France: in INSEE), and $Z_c$ makes the probabilities sum to 1. For the world, the page mixes the countries by their population $P_c$, with $P=\sum_c P_c$:

$$p(f,l)=\sum_c\frac{P_c}{P}\,p(f,l\mid c)$$

The page ranks by $p(f,l)$, and shows $P\,p(f,l)$ as the people expected. In a country, it uses $p(f,l\mid c)$ and $P_c\,p(f,l\mid c)$. The correction learns which names go together. Four names in the United States, with the last names swapped:

Full name Expected, two names alone Expected, model Accounts
Jose Garcia 26,900 91,100 (×3.4) 6,182
Minh Nguyen 18 2,530 (×142) 277
Jose Nguyen 3,400 313 (×0.09) 2
Minh Garcia 141 53 (×0.38) 0

Training. We split the people at random: 80% to train, 10% to calibrate, 10% to test. In France, we keep only the people whose two names are in INSEE (71.5%). This gives 278 million people to train, 35 million to calibrate and 35 million to test. Only the 1,339,826 names of 20 people or more get a vector; the vector of the other names is 0. The training uses $\beta_c=1$, $b_c=0$, and minimizes the negative log-likelihood of the train people. The base does not change, thus

$$\ell=-\frac1N\sum_{i=1}^N\mathbb 1[(f_i,l_i)\in S_{c_i}]\,s_{c_i}(f_i,l_i)+\sum_c w_c\log Z_c$$ $$Z_c=1-\rho_c+\rho_c\,\mathbb E_{(f,l)\sim p_0(\cdot\mid S_c)}\!\left[e^{s_c(f,l)}\right]$$

with $w_c$ the share of the train people in $c$ and $\rho_c=p_0(S_c\mid c)$. The sets $S_c$ have 3 million million pairs in all, so each step estimates the expectation by self-normalized importance sampling, as in Bengio and Senécal: $J=4096$ pairs $x_j$ per country, from a mix $q_c$ of the train people of $c$, of $p_0$ on $S_c$, and of the names of $c$ in proportion to their pairs:

$$\mathbb E\!\left[e^{s_c}\right]\approx\frac{\sum_j r_j\,e^{s_c(x_j)}}{\sum_j r_j},\qquad r_j=\frac{p_0(x_j\mid S_c)}{q_c(x_j)}\le 3$$

The optimizer is AdamW: $10^4$ steps, batches of 65,536 people, learning rate $10^{-2}$, weight decay 0.1. This takes 2.3 minutes on a laptop GPU (Apple M4 Max). Then the GPU computes the exact $Z_c$.

Calibration. As with the neural networks of Guo et al., the trained correction is too strong. For each country, $\beta_c$ and $b_c$ maximize the gain on the calibration people:

$$G_c(\beta,b)=\beta E_c+bQ_c-\log\bigl(1-\rho_c+e^{b}M_c(\beta)\bigr)$$ $$M_c(\beta)=\sum_{(f,l)\in S_c}p_0(f,l\mid c)\,e^{\beta s_c(f,l)}$$

with $E_c$ the mean of $\mathbb 1_{S_c}s_c$ and $Q_c$ the share in $S_c$ of these people. The best offset is $e^{b^\star}=Q_c(1-\rho_c)/\bigl((1-Q_c)M_c(\beta)\bigr)$, and a search on $\beta\in\{0,0.01,\dots,2\}$ gives the scale. The median $\beta_c$ is 0.88. $\beta=0$ gives the base, so no country gets worse than the base. France keeps $\beta=1$ and $b=0$: its calibration people all have their two names in INSEE, so they are not a sample of France.

Result. On the 35 million test people, the model needs 25.117 bits per person, against 25.572 for the two names alone: 0.454 bits less. All 105 countries are better, from 0.03 bits (Uruguay) to 3.3 bits (Burkina Faso).

How do the data and the model fit in a browser?

The browser keeps the files for the next searches. The search and the model run in your browser: the page does not send your name.