Note that $$$10\,000$$$ looks like a lot of characters for an expression, but for $$$n$$$ up to $$$50$$$, due to the particular restrictions of the Deranged Calculator language, this limit is quite tight. So for everything we come up with, we have to be sure we are not wasting too many characters. The model solution uses $$$5969$$$ characters, and there are even better methods that achieve (way) fewer characters.
Let's first try to solve an easier problem: what if, instead of the number of derangements of $$$n$$$ elements, we try to calculate the number of permutations, $$$n!$$$, instead?
We can immediately notice that if we want to use integer constants in our program, we have to make them using workarounds such as n/n or (n+n+n)/n. To save some length, we can precompute a dynamic programming table, which tells us a small construction for the constant $$$i$$$ for $$$1\leq i\leq50$$$. Transitions can be coded to use + and * operators, considering all ways to make $$$i=x+y$$$ or $$$i=x\cdot y$$$, with some appropriate use of parentheses and /n. It turns out that if the the other parts of the expression are well optimized, even writing constants naively in the form $$$(n+n+\dots+n)/n$$$ can work. In less optimized expressions, optimizing expressions for constants is maybe required
Next up, we have to face the issue that we cannot use recursion or loops to calculate the factorial. The one saving grace is that the round function can help us to make some kind of step functions so we can filter out specific $$$n$$$ for our needs.
It would be great if we could make the following program:
(1*[n>=2]+1)*(2*[n>=3]+1)*...*(49*[n>=50]+1)
In such a program, we basically compute $$$50!$$$, but we modulate which terms we want to multiply and which we replace with $$$1$$$. Here [n>=2] is an indicator expression that should output $$$1$$$ if the given inequality is true and $$$0$$$ otherwise.
So now our goal has shifted to building an indicator expression [n>=x] for a given constant $$$x$$$. Here lots of tricks can be used involving the round function. One particularly nice one is the following: round(n/(n+x)). For a given constant $$$x$$$ (for which we have a short expression precomputed), this function gives exactly $$$1$$$ once $$$n\geq x$$$, and $$$0$$$ for $$$2\leq n \lt x$$$. So this solves calculating factorials in the DC language in a reasonable number of characters (this depends on how optimized your round-based "if statement" is, and how well optimized the DP is).
But what about derangements? It turns out there are some nice formulas relating factorials and derangements. One particular fact is that
$$$ \operatorname{round}(n!/e)=D_n, $$$where $$$e\approx2.71828$$$ is Euler's number, and $$$D_n$$$ is the number of derangements for this $$$n$$$. This means we can basically reuse all our work for the factorial case, but now we have to find a way to get a sufficiently precise approximation of $$$e$$$ in our program. There are again many possibilities, but as we already know how to create all integer constants up to $$$50$$$ with a small number of characters, one way is to use the Taylor expansion of $$$e^x$$$, evaluated at $$$x=1$$$:
$$$ e^1=\sum\limits_{k=0}^{\infty}\frac{1}{k!}. $$$We can cut off the series at some point, and as we are dividing $$$50!$$$ by $$$e$$$, it makes sense that we need roughly the first $$$50$$$ terms of the expansion, as we need a relative error of $$$\sim \frac{1}{50!}$$$. Summing all the terms from $$$k=0$$$ to $$$k=50$$$ can be confirmed to work. Naively writing a sum of the inverse factorials will use $$$\sim50^2/2$$$ constants, and this will probably not fit. Instead, we can rewrite the sum as
$$$ 2+\frac{1}{2}\left(1+\frac{1}{3}\left(1+\frac{1}{4}(\dots)\right)\right), $$$making use of the common terms in the factorials. This only uses $$$\sim50$$$ constants, and some $$$1$$$'s, which are very cheap.
Taking this all together, we can write our final solution as round(n!/e), with our expressions for $$$n!$$$ and $$$e$$$ substituted.
The model solution tries to do some more tricks to optimize the precomputation DP for the constants, but they are not needed:
- Use a bigger DP table $$$DP_{i,j}$$$, for $$$0\leq j \lt 4$$$, which calculates a small-length expression for getting an expression with value $$$i\cdot n^j$$$, and also consider dividing using
/n as transitions. - Think about when exactly you can skip parentheses.
- Try to also use the
- operator in your DP. Due to both - and / transitions, the DP is no longer acyclic, but as the table is very small, we can just keep iterating through all possible transitions multiple times, and eventually the DP table stays unchanged.
As a bonus, what is the lowest you can push the number of characters?