Decode Factor Trees

Wolfram Language (Mathematica), 52 45 bytes

[email protected]*StringReplace[{"["->"Prime[1"}]

Try it online!

Input uses brackets.

Transforms the input into a Mathematica expression that computes the result. We do this simply by replacing [ with Prime[1. This works because concatenation is multiplication in Mathematica.


Prolog (SWI), 134 128 127 124 bytes

This answer is part of a collaboration between myself and 0'. We both worked on this together, the only reason I'm posting it is because I won Rock, Paper, Scissors.

\Q-->{Q=1};"(",\N,")",\B,{findnsols(N,I,(between(2,inf,I),\+ (between(3,I,U),0=:=I mod(U-1))),L)->append(_,[Y],L),Q is Y*B}.

Try it online!

Explanation

This answer is a perfect exemplar of what makes golfing in prolog fun.


This answer uses Prologs powerful system for definite clause grammars. Here is our grammar ungolfed a bit.

head(1)-->[].
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
prime(N,Y):-
  findnsols(N,I,(
    between(2,inf,I),
    isprime(I)
  ),L),
  append(_,[Y],L),!.

The first construction rule is:

head(1)-->[].

This tells Prolog that the empty string corresponds to 1.

Our second rule of construction is a tiny bit more complex.

head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.

This tells us that any non empty string contains parentheses around a clause with these same rules, to the right of a clause with these same rules.

It also tells us that the value of this clause (Q) follows the rule:

{prime(N,Y),Q is Y*B}

Breaking this down, Q is the product of 2 numbers Y and B. B is just the value of the clause to the left and Y is the Nth prime where N is the value of the clause inside the parentheses.

This rule covers both of the formation rules of the factor tree

  • Concatenation multiplies
  • Enclosure takes the nth prime

Now for the predicate definitions. In the ungolfed version there are two predicates at play (in my actual code I've forward chained the predicates away). The two relevant predicates here are isprime/1, which matches a prime number, and prime/2, which, given N and Y, matches iff Y is the Nth prime. First we have

isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).

This works of a pretty standard definition of primality, we insist that there is no number between 2 and I, including 2 but not I that divides I.

The next predicate is also pretty simple

prime(N,Y):-
  findnsols(N,I,(
    between(2,inf,I),
    isprime(I)
  ),L),
  append(_,[Y],L),!.

We use findnsols to find the first N numbers that are prime, we then return the last one. The trick here is that while findnsols is not guaranteed to find the smallest N primes, because of the way SWI handles between it will always find smaller primes sooner. This however means we have to cut to prevent it from finding more primes.


The golfs

We can forward reason in our code twice. Since isprime is only used once its definition can be moved inside of prime. The next one is to move prime directly inside of the DCG, however since we use a cut in prime to prevent findnsols from producing too many primes we have a bit of an issue. The cut, cuts the entire DCG instead of just the bit we want. After a bit of documentation digging we found that once/1 could be used to cut just this portion but not the entire DCG. However more documentation digging revealed that the -> operator could also be used to perform a similar task. The -> operator is roughly equivalent to ,!, so we moved our cut to the other side of append/3 and replaced it with ->.

In SWI-Prolog predicates (and rules) can be give operators as names which allows us to drop the parentheses normally required. Thereby we can save 6 bytes by calling the rule \.


Python 3, 125 110 bytes

lambda s:eval(s.replace("]","1]*").replace("[","p[1*")+"1")
p=2,
k=P=1
while k<1e4:
 if P%k:p+=k,
 P*=k*k;k+=1

Try it online!

Uses xnor's implementation of the Wilson Theorem method to generate primes. Substitutes () with [].