[BASE Cinque - Appunti di Matematica ricreativa]

Un programma per risolvere il gioco dei quattro numeri

Sintesi. Siete capaci di combinare i numeri 3, 3, 5, 5 e le operazioni aritmetiche (+, -, *, /) per ottenere 18?
Questo tipo di puzzle appartiene alla famiglia dei "giochi dei quattro numeri". Sono piccoli enigmi che nascondono una sorprendente ricchezza combinatoria.
Vediamo come costruire un programma che li esplori (quasi) tutti, dai casi più semplici fino a varianti più generali e complesse.

Qualche esempio per capire il contesto

Nota. Usiamo * per la moltiplicazione e / per la divisione, come nei linguaggi di programmazione e nella Matematica testuale.

La scrittura a / b può indicare una divisione o una frazione.

 

1. Un classico: i quattro 4
Usando quattro 4, le quattro operazioni e le parentesi, si possono ottenere i numeri interi da 0 a 9. Non è invece possibile ottenere 10.

Ecco una lista di possibili soluzioni.

0 = 4 + 4 - 4 - 4

1 = 4 - 4 + (4 / 4)

2 = 4 / 4 + 4 / 4

3 = (4 + 4 + 4) / 4

4 = 4 + 4 * (4 - 4)

5 = (4 * 4 + 4) / 4

6 = 4 + (4 + 4) / 4

7 = 4 + 4 - (4 / 4)

8 = 4 + 4 + 4 - 4

9 = 4 + 4 + (4 / 4)

 

2. Difficile per la IA: il problema 3, 3, 5, 5

Usando 3, 3, 5, 5 e le quattro operazioni, si può ottenere 18.

Soluzione.

5 * (3 + 3 / 5) = 5 * (18 / 5) = 18

Nota. Questo esercizio ha creato qualche difficoltà ad alcuni sistemi di IA di base (luglio 2026).

 

3. Una leggenda: il gioco del 24

Usando 3, 3, 8, 8 e le quattro operazioni, si può ottenere 24.

Soluzione.

8 / (3 - 8 / 3) = 8 / (1 / 3) = 24

La difficoltà deriva dal fatto che l'unica soluzione possibile richiede l'uso delle frazioni e della divisione uscendo dal campo dei soli numeri interi. Secondo una leggenda metropolitana, questo esercizio sarebbe stato usato nei colloqui tecnici di Google.

 

4. Quando le regole non bastano: il problema 2, 2, 2, 2

Usando i numeri 2, 2, 2, 2, le operazioni aritmetiche e le parentesi non si può ottenere 9.

Possiamo estendere le regole, per esempio ammettendo la concatenazione di due o più cifre per formare un numero a più cifre.

Soluzione.

 22 / 2 - 2 = 9

 

5. Un problema storico: con quattro 3

Jack dice a suo fratello Harry: "Io sono capace di combinare quattro 3 e alcune operazioni aritmetiche in modo da ottenere esattamente 34. Tu sei capace di fare altrettanto?"

(Thomas Dilworth, Schoolmaster's Assistant, 1743)

Soluzione.

33 + 3/3 = 34

Le regole di base del gioco

Scriviamo chiaramente le regole del gioco nella versione base.

Obiettivo del gioco

Ottenere uno o più valori bersaglio combinando 4 numeri dati tramite le operazioni aritmetiche e le parentesi.

Regole di Composizione

Numeri.

Operazioni.

Parentesi.

L'algoritmo essenziale

Vogliamo scrivere un programma che generi, valuti e stampi tutte le espressioni possibili. Ogni espressione sarà formata da 4 numeri e 3 operatori binari.

Cominciamo col pensare un algoritmo per eseguire una ricerca esaustiva di utte le possibili espressioni aritmetiche valide seguendo quattro passaggi fondamentali:

  1. Disposizioni di numeri e operatori. L'algoritmo dovrà generare le 24 permutazioni dei 4 numeri (4!) e le 64 disposizioni con ripetizione dei 3 operatori (4^3). In tutto sono 1536 casi. Conviene gestire le espressioni in notazione RPN (Reverse Polish Notation) perché non usa le parentesi ed è più semplice da elaborare.
  2. Strutture RPN. Valuta ciascun caso attraverso le 5 strutture RPN valide, che rappresentano in modo univoco ed esaustivo tutti i possibili modi di disporre le parentesi. In tutto sono 7680 casi (1536 * 5).
  3. Calcolo e validazione. Calcola le sequenze RPN tramite un algoritmo a stack (pila), memorizzando i risultati validi (numeri naturali) ed escludendo le operazioni non consentite (come la divisione per zero).
  4. Output. Ricostruzione e stampa delle espressione complete di parentesi.

Il programma di base in Decimal BASIC

Ho scelto il linguaggio Decimal BASIC prima di tutto perché lo conosco bene e mi piace e in secondo luogo perché gestisce nativamente i numeri razionali e gli interi a grandezza arbitraria.

Se volete, potete tradurlo facilmente nel vostro linguaggio di programmazione preferito.

Trovate il codice nell'appendice 1.

Mettiamo alla prova il programma di base

Di seguito, riportiamo solo i primi risultati per alcune quaterne di numeri iniziali.

1) Con 1,1,1,1

Risultati trovati:
------------------
0 = (1 + (1 - (1 + 1)))
1 = (1 + (1 - (1 * 1)))
2 = (1 + (1 + (1 - 1)))
3 = (1 + (1 + (1 * 1)))
4 = (1 + (1 + (1 + 1)))

 

2) Con 3,3,3,3

Risultati trovati:
------------------
0 = (3 + (3 - (3 + 3)))
1 = (((3 + 3) - 3) / 3)
2 = (((3 * 3) - 3) / 3)
3 = (((3 + 3) + 3) / 3)
4 = ((3 + (3 * 3)) / 3)
5 = (3 + ((3 + 3) / 3))
6 = (3 + (3 + (3 - 3)))
7 = (3 + (3 + (3 / 3)))
8 = ((3 * 3) - (3 / 3))
9 = (((3 + 3) - 3) * 3)
10 = ((3 * 3) + (3 / 3))

 

3) Con 4,4,4,4

Risultati trovati:
------------------
0 = (4 + (4 - (4 + 4)))
1 = (((4 + 4) - 4) / 4)
2 = (4 - ((4 + 4) / 4))
3 = (((4 + 4) + 4) / 4)
4 = (4 + ((4 - 4) * 4))
5 = ((4 + (4 * 4)) / 4)
6 = (4 + ((4 + 4) / 4))
7 = (4 + (4 - (4 / 4)))
8 = (4 + (4 + (4 - 4)))
9 = (4 + (4 + (4 / 4)))
10 = non possibile

 

4) Con 3,3,5,5

Risultati trovati:
------------------
0 = ((3 + 3) * (5 - 5))
1 = ((3 - 3) + (5 / 5))
2 = ((3 / 3) + (5 / 5))
3 = (3 + (3 * (5 - 5)))
4 = ((3 - 5) * (3 - 5))
5 = (((3 + 3) - 5) * 5)
6 = (3 + (3 + (5 - 5)))
7 = (3 + (3 + (5 / 5)))
8 = ((3 * 3) - (5 / 5))
9 = (3 * (3 + (5 - 5)))
10 = ((3 - 3) + (5 + 5))
11 = ((3 / 3) + (5 + 5))
12 = ((3 - (3 / 5)) * 5)
13 = (3 + ((3 * 5) - 5))
14 = non possibile
15 = non possibile
16 = (3 + (3 + (5 + 5)))
17 = (((3 * 5) - 3) + 5)
18 = ((3 + (3 / 5)) * 5)
19 = ((3 * 3) + (5 + 5))
20 = (((3 * 3) - 5) * 5)

 

5) Con 2,4,6,8

Risultati trovati:
------------------
0 = (((2 + 4) - 6) * 8)
1 = ((2 - 4) / (6 - 8))
2 = (((2 * 6) + 4) / 8)
3 = ((2 + 4) / (8 - 6))
4 = (2 + (4 + (6 - 8)))
5 = (2 + (4 * (6 / 8)))
6 = ((2 * 4) + (6 - 8))
7 = ((2 / (4 - 6)) + 8)
8 = (2 + ((4 - 6) + 8))
9 = (((2 + 4) / 6) + 8)
10 = (2 - (4 * (6 - 8)))

Necessità di aggiungere operatori

Avete certamente notato che con alcune quaterne di numeri non è possibile ottenere tutti i risultati da 0 a 10.
E con quasi tutte le quaterne è impossibile coprire l'intervallo da 0 a 20.

Diventa quindi irresistibile la tentazione di introdurre nuovi operatori matematici per ampliare la gamma dei numeri raggiungibili.

  1. Aggiungiamo quindi al programma di base altri tre operatori classici di questo gioco:
    Concatenazione (&): unisce due numeri da una cifra per formare un numero da due cifre.
    E' un operatore binario. Si applica solo ai numeri iniziali, mai a numeri derivanti da operazioni intermedie.
    a & b = 10 * a + b (esempio 3 & 5 = 35)
  2. Forma decimale (D): divide per 10 un numero n da una cifra e lo rappresenta testualmente nella forma .n.
    E' un operatore unario. Si applica solo ai numeri iniziali,
    D(n) = n/10 = .n  (esempio D(5) = 5/10 = 0.5 = .5
  3. Forma periodica (R): divide per 9 un numero n da una cifra e lo rappresenta testualmente nella forma .(n).
    E' un operatore unario. Si applica solo ai numeri iniziali,
    R(n) = n/9 = .(n)  (esempio R(5) = 5/9 = 0.55555... = .(5)

In modalità ARITHMETIC RATIONAL le divisioni n/10 e n/9 sono gestite come frazioni esatte, evitando errori di arrotondamento.

Trovate il codice nell'appendice 2.

Priorità ai risultati semplici

Questa nuova versione del programma genera le soluzioni in due fasi a complessita crescente:

 Ricordate però che il programma stampa solo la prima soluzione trovata per ogni numero obiettivo.

Perciò potrebbero non essere visibili soluzioni più semplici oppure più interessanti.

Note sul numero di casi esaminati dal programma

Per esplorare l'intero spazio delle soluzioni, il programma valuta un numero elevato di combinazioni:

In tutto sono:

Espressioni RPN versus espressioni standard

Questo è un punto chiave del programma, che andrebbe approfondito.
Qui vi lascio solo uno spunto.

Il numero di modi diversi in cui è possibile parentesizzare un'espressione con n operatori binari applicati a n+1 operandi è l'ennesimo numero di Catalan, cioè:
Cn = (2n)! / ((n! * n!)*(n+1))

Per n=3, ovvero 3 operatori e 4 operandi, abbiamo:

Cn = 6! / ((3! * 3!)*4 = 5

La tabella seguente mostra, attraverso un esempio pratico, i 5 tipi possibili di espressioni.

In questo esempio, i numeri sono 9, 1, 3, 2 e gli operatori +, *, -.

Tipo Versione RPN Versione Algebrica Risultato
1 9 1 + 3 * 2 - ((9 + 1) * 3) - 2 28
2 9 1 + 3 2 * - (9 + 1) - (3 * 2) 4
3 9 1 3 + * 2 - (9 * (1 + 3)) - 2 34
4 9 1 3 + 2 * - 9 - ((1 + 3) * 2) 1
5 9 1 3 2 + * - 9 - (1 * (3 + 2)) 4

Mettiamo alla prova il programma avanzato

Di seguito, riportiamo i primi risultati per alcune quaterne di numeri iniziali.

1) Con 1,1,1,1

Trova espressioni per tutti i numeri obiettivo da 0 a 10 e oltre.

Risultati trovati:
------------------
0 = (1 + (1 - (1 + 1)))
1 = (1 + (1 - (1 * 1)))
2 = (1 + (1 + (1 - 1)))
3 = (1 + (1 + (1 * 1)))
4 = (1 + (1 + (1 + 1)))
5 = (1 / ((1 + 1) * .1))
6 = (1 + (1 / (.1 + .1)))
7 = ((1 / .(1)) - (1 + 1))
8 = (1 * ((1 / .(1)) - 1))
9 = (11 - (1 + 1))
10 = (1 * (11 - 1))
11 = (1 + (11 - 1))
12 = (1 + (1 * 11))
13 = (1 + (1 + 11))
14 = non possibile
15 = non possibile
16 = non possibile
17 = (((1 + 1) / .(1)) - 1)
18 = ((1 + 1) * (1 / .(1)))

2) Con 4, 4, 4, 4

Trova tutte le soluzioni da 1 a 20 e oltre.
il primo numero che non riesce a risolvere è 23.

Risultati trovati:
------------------
In particolare:
10 = ((44 - 4) / 4)

3) Con 3, 3, 3, 3

Trova tutte le soluzioni da 1 a 20 e oltre.

Risultati trovati:
------------------
In particolare:
14 = (3 + (33 / 3))
15 = (3 + (3 + (3 * 3)))

4) Con 0,1,9,9

Risultati trovati:
------------------
0 = (0 + (1 - (9 / 9)))
1 = (0 + (1 + (9 - 9)))
2 = (0 + (1 + (9 / 9)))
3 = (0 + (1 + (.(9) + .(9))))
4 = non possibile
5 = (10 / (.(9) + .(9)))
6 = non possibile
7 = ((0 - 1) + (9 - .(9)))
8 = (((0 * 9) - 1) + 9)
9 = ((0 * (1 + 9)) + 9)
10 = (0 + (19 - 9))

Per ora mi fermo qui e riporto di seguito in codici dei due programmi.

Appendice 1. Programma base per il gioco dei 4 numeri

DECLARE EXTERNAL SUB VALUTA_RPN 
OPTION ARITHMETIC RATIONAL
OPTION BASE 0 
! =============================================================
! PROGRAMMA PRINCIPALE (VERSIONE 10/08/26)
! =============================================================
! Le quattro cifre iniziali
DIM CIFRE(4)
DATA 4,4,4,4              
FOR I = 1 TO 4
   READ CIFRE(I)
NEXT I

! Gli operatori binari ammessi
DIM OP$(4)
LET OP$(1) = "+"
LET OP$(2) = "-"
LET OP$(3) = "*"
LET OP$(4) = "/"

! Array per contenere la prima soluzione trovata
! per ciascun numero, da 0 a 1000
DIM SOLUZIONE$(1000)

PRINT "Ricerca in corso..."

! 1. Ciclo su tutte le permutazioni dei 4 operandi (4! = 24)
FOR I1 = 1 TO 4
   FOR I2 = 1 TO 4
      IF I2 <> I1 THEN
         FOR I3 = 1 TO 4
            IF I3 <> I1 AND I3 <> I2 THEN
               FOR I4 = 1 TO 4
                  IF I4 <> I1 AND I4 <> I2 AND I4 <> I3 THEN
                   
                     DIM A(4)
                     LET A(1) = CIFRE(I1)
                     LET A(2) = CIFRE(I2)
                     LET A(3) = CIFRE(I3)
                     LET A(4) = CIFRE(I4)
                      
                     ! 2. Ciclo su tutte le disposizioni con ripetizione di 3 operatori (4^3 = 64)
                     FOR O1 = 1 TO 4
                        FOR O2 = 1 TO 4
                           FOR O3 = 1 TO 4
                            
                              DIM OP_SEL$(3)
                              LET OP_SEL$(1) = OP$(O1)
                              LET OP_SEL$(2) = OP$(O2)
                              LET OP_SEL$(3) = OP$(O3)
                               
                              ! 3. Test delle 5 strutture RPN valide
                              FOR TIPO = 1 TO 5
                                 CALL VALUTA_RPN(a, op_sel$, tipo, soluzione$)
                              NEXT TIPO
                               
                           NEXT O3
                        NEXT O2
                     NEXT O1
                      
                  END IF
               NEXT I4
            END IF
         NEXT I3
      END IF
   NEXT I2
NEXT I1

! 4. Stampa dei risultati per i primi numeri naturali trovati
PRINT
PRINT "RISULTATI TROVATI:"
PRINT "------------------"
FOR N = 0 TO 1000
   IF SOLUZIONE$(N) <> "" THEN
      PRINT STR$(N) & " = " & SOLUZIONE$(N)
   END IF
NEXT N

!Fine del programma principale
END

! =============================================================
! SUBROUTINE PER LA VALUTAZIONE DELLA STRUTTURA RPN
! =============================================================
!a(): contiene la permutazione corrente dei 4 numeri dati.
!Passato per riferimento.
!----------------------------------
!op_sel$(): contiene la combinazione corrente dei 3 operatori (stringhe)
!scelti per il calcolo. Passato per riferimento.
!----------------------------------
!tipo: identifica quale delle 5 strutture RPN valide si deve elaborare (numero).
!Passato per valore.
!----------------------------------
!soluzione$(): lista (ARRAY) globale delle soluzioni (stringhe) trovate
!per ogni numero naturale raggiungibile. Passata per riferimento.
!----------------------------------

EXTERNAL SUB VALUTA_RPN(a(), op_sel$(), tipo, soluzione$())

! TOKENS, i 7 elementi individuali (4 numeri e 3 operatori)
!che compongono l'espressione RPN
DIM tokens$(7)

! Assegnazione dei TOKENS in base alla struttura RPN selezionata
SELECT CASE tipo
CASE 1
! Struttura 1: A1 op1 (A2 op2 (A3 op3 A4))
! RPN: A1 A2 A3 A4 op3 op2 op1
   LET tokens$(1) = STR$(a(1))
   LET tokens$(2) = STR$(a(2))
   LET tokens$(3) = STR$(a(3))
   LET tokens$(4) = STR$(a(4))
   LET tokens$(5) = op_sel$(3)
   LET tokens$(6) = op_sel$(2)
   LET tokens$(7) = op_sel$(1)
    
CASE 2
! Struttura 2: (A1 op1 A2) op2 (A3 op3 A4)
! RPN: A1 A2 op1 A3 A4 op3 op2
   LET tokens$(1) = STR$(a(1))
   LET tokens$(2) = STR$(a(2))
   LET tokens$(3) = op_sel$(1)
   LET tokens$(4) = STR$(a(3))
   LET tokens$(5) = STR$(a(4))
   LET tokens$(6) = op_sel$(3)
   LET tokens$(7) = op_sel$(2)
    
CASE 3
! Struttura 3: A1 op1 ((A2 op2 A3) op3 A4)
! RPN: A1 A2 A3 op2 A4 op3 op1
   LET tokens$(1) = STR$(a(1))
   LET tokens$(2) = STR$(a(2))
   LET tokens$(3) = STR$(a(3))
   LET tokens$(4) = op_sel$(2)
   LET tokens$(5) = STR$(a(4))
   LET tokens$(6) = op_sel$(3)
   LET tokens$(7) = op_sel$(1)
    
CASE 4
! Struttura 4: ((A1 op1 A2) op2 A3) op3 A4
! RPN: A1 A2 op1 A3 op2 A4 op3
   LET tokens$(1) = STR$(a(1))
   LET tokens$(2) = STR$(a(2))
   LET tokens$(3) = op_sel$(1)
   LET tokens$(4) = STR$(a(3))
   LET tokens$(5) = op_sel$(2)
   LET tokens$(6) = STR$(a(4))
   LET tokens$(7) = op_sel$(3)
    
CASE 5
! Struttura 5: (A1 op1 (A2 op2 A3)) op3 A4
! RPN: A1 A2 A3 op2 op1 A4 op3
   LET tokens$(1) = STR$(a(1))
   LET tokens$(2) = STR$(a(2))
   LET tokens$(3) = STR$(a(3))
   LET tokens$(4) = op_sel$(2)
   LET tokens$(5) = op_sel$(1)
   LET tokens$(6) = STR$(a(4))
   LET tokens$(7) = op_sel$(3)
END SELECT

! Calcolo dello stack (pila) RPN
DIM stack(10)
DIM stack_txt$(10)

! Stack Pointer, indica l'elemento in cima alla pila
LET sp = 0  

FOR i = 1 TO 7
   LET t$ = tokens$(i)
    
   IF t$ = "+" OR t$ = "-" OR t$ = "*" OR t$ = "/" THEN
   ! Errore di stack, meno di 2 operandi disponibili
      IF sp < 2 THEN EXIT SUB  
      
      ! LIFO: estraiamo prima B e poi A per ripristinare ordine corretto (A op B)      
      LET val_b = stack(sp)
      LET txt_b$ = stack_txt$(sp)
      LET sp = sp - 1
       
      LET val_a = stack(sp)
      LET txt_a$ = stack_txt$(sp)
       
      ! Calcolo perazione corrente tramite SELECT CASE
      SELECT CASE t$
      CASE "+"
         LET res = val_a + val_b
      CASE "-"
         LET res = val_a - val_b
      CASE "*"
         LET res = val_a * val_b
      CASE "/"
      ! Controllo divisione per zero non valida
         IF val_b = 0 THEN EXIT SUB  
         LET res = val_a / val_b
      END SELECT
       
      ! Salva il risultato numerico e la sotto-espressione formattata 
      ! con parentesi
      LET stack(sp) = res
      LET stack_txt$(sp) = "(" & txt_a$ & " " & t$ & " " & txt_b$ & ")"
   ELSE
   ! È un numero: lo spingiamo (PUSH) in cima allo stack
      LET sp = sp + 1
      LET stack(sp) = VAL(t$)
      LET stack_txt$(sp) = t$
   END IF
NEXT i

! Salvataggio del risultato finale
! Un solo elemento rimasto: espressione RPN valida

IF sp = 1 THEN 
   LET res_finale = stack(1)
    
   ! Filtra solo i numeri naturali (interi >= 0)
   IF res_finale >= 0 AND res_finale = INT(res_finale) THEN
      LET n = res_finale
      IF n <= 1000 THEN
      ! Registra la soluzione solo se per quel valore N
      !non ne è già stata trovata una
         IF soluzione$(n) = "" THEN
            LET soluzione$(n) = stack_txt$(1)
         END IF
      END IF
   END IF
END IF
END SUB

Appendice 2. Programma Avanzato per il gioco dei 4 numeri.

! =============================================================
! PROGRAMMA PRINCIPALE (VERSIONE 10/08/26)
! =============================================================

DECLARE EXTERNAL SUB valuta_rpn 
OPTION ARITHMETIC RATIONAL
OPTION BASE 0 

! Dichiarazione degli array
DIM cifre(4)
DIM op$(5)
DIM soluzione$(1000)
DIM a(4)
DIM a_txt$(4)
DIM a_orig(4)
DIM op_sel$(3)

! Inizializzazione cifre iniziali
DATA 2,2,2,2               
FOR i = 1 TO 4
   READ cifre(i)
NEXT i

! Gli operatori binari ammessi (inclusa la concatenazione "&")
LET op$(1) = "+"
LET op$(2) = "-"
LET op$(3) = "*"
LET op$(4) = "/"
LET op$(5) = "&"

! =============================================================
! FASE 1: Ricerca SENZA operatori unari D(n) e R(n)
! =============================================================
PRINT "Fase 1: Ricerca con sole operazioni base e concatenazione..."

FOR i1 = 1 TO 4
   FOR i2 = 1 TO 4
      IF i2 <> i1 THEN
         FOR i3 = 1 TO 4
            IF i3 <> i1 AND i3 <> i2 THEN
               FOR i4 = 1 TO 4
                  IF i4 <> i1 AND i4 <> i2 AND i4 <> i3 THEN
                   
                  ! Configurazione operandi (cifre pure)
                     LET a(1) = cifre(i1)
                     LET a_txt$(1) = STR$(cifre(i1))
                     LET a_orig(1) = 1
                      
                     LET a(2) = cifre(i2)
                     LET a_txt$(2) = STR$(cifre(i2))
                     LET a_orig(2) = 1
                      
                     LET a(3) = cifre(i3)
                     LET a_txt$(3) = STR$(cifre(i3))
                     LET a_orig(3) = 1
                      
                     LET a(4) = cifre(i4)
                     LET a_txt$(4) = STR$(cifre(i4))
                     LET a_orig(4) = 1
                      
                     ! Ciclo sugli operatori binari
                     FOR o1 = 1 TO 5
                        FOR o2 = 1 TO 5
                           FOR o3 = 1 TO 5
                            
                              LET op_sel$(1) = op$(o1)
                              LET op_sel$(2) = op$(o2)
                              LET op_sel$(3) = op$(o3)
                               
                              ! Test delle 5 strutture RPN
                              FOR tipo = 1 TO 5
                                 CALL valuta_rpn(a, a_txt$, a_orig, op_sel$, tipo, soluzione$)
                              NEXT tipo
                               
                           NEXT o3
                        NEXT o2
                     NEXT o1
                      
                  END IF
               NEXT i4
            END IF
         NEXT i3
      END IF
   NEXT i2
NEXT i1

! =============================================================
! FASE 2: Ricerca CON operatori unari per i numeri non ancora trovati
! =============================================================
PRINT "Fase 2: Applicazione di Decimale D(n) e Periodico R(n)..."

FOR i1 = 1 TO 4
   FOR i2 = 1 TO 4
      IF i2 <> i1 THEN
         FOR i3 = 1 TO 4
            IF i3 <> i1 AND i3 <> i2 THEN
               FOR i4 = 1 TO 4
                  IF i4 <> i1 AND i4 <> i2 AND i4 <> i3 THEN
                   
                  ! Ciclo sulle trasformazioni unarie
                     FOR m1 = 0 TO 2
                        FOR m2 = 0 TO 2
                           FOR m3 = 0 TO 2
                              FOR m4 = 0 TO 2
                               
                              ! Salta la combinazione 0,0,0,0 perché già elaborata nella Fase 1
                                 IF m1 + m2 + m3 + m4 > 0 THEN
                                  
                                 ! Configurazione operando 1
                                    IF m1 = 0 THEN
                                       LET a(1) = cifre(i1)
                                       LET a_txt$(1) = STR$(cifre(i1))
                                       LET a_orig(1) = 1
                                    ELSEIF m1 = 1 THEN
                                       LET a(1) = cifre(i1) / 10
                                       LET a_txt$(1) = "." & STR$(cifre(i1))
                                       LET a_orig(1) = 0
                                    ELSE
                                       LET a(1) = cifre(i1) / 9
                                       LET a_txt$(1) = ".(" & STR$(cifre(i1)) & ")"
                                       LET a_orig(1) = 0
                                    END IF
                                     
                                    ! Configurazione operando 2
                                    IF m2 = 0 THEN
                                       LET a(2) = cifre(i2)
                                       LET a_txt$(2) = STR$(cifre(i2))
                                       LET a_orig(2) = 1
                                    ELSEIF m2 = 1 THEN
                                       LET a(2) = cifre(i2) / 10
                                       LET a_txt$(2) = "." & STR$(cifre(i2))
                                       LET a_orig(2) = 0
                                    ELSE
                                       LET a(2) = cifre(i2) / 9
                                       LET a_txt$(2) = ".(" & STR$(cifre(i2)) & ")"
                                       LET a_orig(2) = 0
                                    END IF
                                     
                                    ! Configurazione operando 3
                                    IF m3 = 0 THEN
                                       LET a(3) = cifre(i3)
                                       LET a_txt$(3) = STR$(cifre(i3))
                                       LET a_orig(3) = 1
                                    ELSEIF m3 = 1 THEN
                                       LET a(3) = cifre(i3) / 10
                                       LET a_txt$(3) = "." & STR$(cifre(i3))
                                       LET a_orig(3) = 0
                                    ELSE
                                       LET a(3) = cifre(i3) / 9
                                       LET a_txt$(3) = ".(" & STR$(cifre(i3)) & ")"
                                       LET a_orig(3) = 0
                                    END IF
                                     
                                    ! Configurazione operando 4
                                    IF m4 = 0 THEN
                                       LET a(4) = cifre(i4)
                                       LET a_txt$(4) = STR$(cifre(i4))
                                       LET a_orig(4) = 1
                                    ELSEIF m4 = 1 THEN
                                       LET a(4) = cifre(i4) / 10
                                       LET a_txt$(4) = "." & STR$(cifre(i4))
                                       LET a_orig(4) = 0
                                    ELSE
                                       LET a(4) = cifre(i4) / 9
                                       LET a_txt$(4) = ".(" & STR$(cifre(i4)) & ")"
                                       LET a_orig(4) = 0
                                    END IF
                                     
                                    ! Ciclo su tutte le combinazioni di 3 operatori binari
                                    FOR o1 = 1 TO 5
                                       FOR o2 = 1 TO 5
                                          FOR o3 = 1 TO 5
                                           
                                             LET op_sel$(1) = op$(o1)
                                             LET op_sel$(2) = op$(o2)
                                             LET op_sel$(3) = op$(o3)
                                              
                                             ! Test delle 5 strutture RPN
                                             FOR tipo = 1 TO 5
                                                CALL valuta_rpn(a, a_txt$, a_orig, op_sel$, tipo, soluzione$)
                                             NEXT tipo
                                              
                                          NEXT o3
                                       NEXT o2
                                    NEXT o1
                                     
                                 END IF
                                  
                              NEXT m4
                           NEXT m3
                        NEXT m2
                     NEXT m1
                      
                  END IF
               NEXT i4
            END IF
         NEXT i3
      END IF
   NEXT i2
NEXT i1

! =============================================================
! STAMPA RISULTATI
! =============================================================
PRINT
PRINT "RISULTATI TROVATI:"
PRINT "------------------"
FOR n = 0 TO 1000
   IF soluzione$(n) <> "" THEN
      PRINT STR$(n) & " = " & soluzione$(n)
   END IF
NEXT n

END

! =============================================================
! SUBROUTINE PER LA VALUTAZIONE DELLA STRUTTURA RPN
! =============================================================

EXTERNAL SUB valuta_rpn(a(), a_txt$(), a_orig(), op_sel$(), tipo, soluzione$())

! Dichiarazioni degli array all'inizio della SUB
DIM tokens_val(7)
DIM tokens_txt$(7)
DIM tokens_orig(7)
DIM stack(10)
DIM stack_txt$(10)
DIM stack_orig(10)

! Assegnazione dei TOKENS in base alla struttura RPN selezionata
SELECT CASE tipo
CASE 1
! Struttura 1: A1 op1 (A2 op2 (A3 op3 A4))
   LET tokens_val(1) = a(1)
   LET tokens_txt$(1) = a_txt$(1)
   LET tokens_orig(1) = a_orig(1)
   LET tokens_val(2) = a(2)
   LET tokens_txt$(2) = a_txt$(2)
   LET tokens_orig(2) = a_orig(2)
   LET tokens_val(3) = a(3)
   LET tokens_txt$(3) = a_txt$(3)
   LET tokens_orig(3) = a_orig(3)
   LET tokens_val(4) = a(4)
   LET tokens_txt$(4) = a_txt$(4)
   LET tokens_orig(4) = a_orig(4)
   LET tokens_txt$(5) = op_sel$(3)
   LET tokens_txt$(6) = op_sel$(2)
   LET tokens_txt$(7) = op_sel$(1)
    
CASE 2
! Struttura 2: (A1 op1 A2) op2 (A3 op3 A4)
   LET tokens_val(1) = a(1)
   LET tokens_txt$(1) = a_txt$(1)
   LET tokens_orig(1) = a_orig(1)
   LET tokens_val(2) = a(2)
   LET tokens_txt$(2) = a_txt$(2)
   LET tokens_orig(2) = a_orig(2)
   LET tokens_txt$(3) = op_sel$(1)
   LET tokens_val(4) = a(3)
   LET tokens_txt$(4) = a_txt$(3)
   LET tokens_orig(4) = a_orig(3)
   LET tokens_val(5) = a(4)
   LET tokens_txt$(5) = a_txt$(4)
   LET tokens_orig(5) = a_orig(4)
   LET tokens_txt$(6) = op_sel$(3)
   LET tokens_txt$(7) = op_sel$(2)
    
CASE 3
! Struttura 3: A1 op1 ((A2 op2 A3) op3 A4)
   LET tokens_val(1) = a(1)
   LET tokens_txt$(1) = a_txt$(1)
   LET tokens_orig(1) = a_orig(1)
   LET tokens_val(2) = a(2)
   LET tokens_txt$(2) = a_txt$(2)
   LET tokens_orig(2) = a_orig(2)
   LET tokens_val(3) = a(3)
   LET tokens_txt$(3) = a_txt$(3)
   LET tokens_orig(3) = a_orig(3)
   LET tokens_txt$(4) = op_sel$(2)
   LET tokens_val(5) = a(4)
   LET tokens_txt$(5) = a_txt$(4)
   LET tokens_orig(5) = a_orig(4)
   LET tokens_txt$(6) = op_sel$(3)
   LET tokens_txt$(7) = op_sel$(1)
    
CASE 4
! Struttura 4: ((A1 op1 A2) op2 A3) op3 A4
   LET tokens_val(1) = a(1)
   LET tokens_txt$(1) = a_txt$(1)
   LET tokens_orig(1) = a_orig(1)
   LET tokens_val(2) = a(2)
   LET tokens_txt$(2) = a_txt$(2)
   LET tokens_orig(2) = a_orig(2)
   LET tokens_txt$(3) = op_sel$(1)
   LET tokens_val(4) = a(3)
   LET tokens_txt$(4) = a_txt$(3)
   LET tokens_orig(4) = a_orig(3)
   LET tokens_txt$(5) = op_sel$(2)
   LET tokens_val(6) = a(4)
   LET tokens_txt$(6) = a_txt$(4)
   LET tokens_orig(6) = a_orig(4)
   LET tokens_txt$(7) = op_sel$(3)
    
CASE 5
! Struttura 5: (A1 op1 (A2 op2 A3)) op3 A4
   LET tokens_val(1) = a(1)
   LET tokens_txt$(1) = a_txt$(1)
   LET tokens_orig(1) = a_orig(1)
   LET tokens_val(2) = a(2)
   LET tokens_txt$(2) = a_txt$(2)
   LET tokens_orig(2) = a_orig(2)
   LET tokens_val(3) = a(3)
   LET tokens_txt$(3) = a_txt$(3)
   LET tokens_orig(3) = a_orig(3)
   LET tokens_txt$(4) = op_sel$(2)
   LET tokens_txt$(5) = op_sel$(1)
   LET tokens_val(6) = a(4)
   LET tokens_txt$(6) = a_txt$(4)
   LET tokens_orig(6) = a_orig(4)
   LET tokens_txt$(7) = op_sel$(3)
END SELECT

! Esecuzione dello Stack RPN
LET sp = 0

FOR i = 1 TO 7
   LET t$ = tokens_txt$(i)
    
   IF t$ = "+" OR t$ = "-" OR t$ = "*" OR t$ = "/" OR t$ = "&" THEN
   ! Errore di stack
      IF sp < 2 THEN EXIT SUB
       
      LET val_b = stack(sp)
      LET txt_b$ = stack_txt$(sp)
      LET orig_b = stack_orig(sp)
      LET sp = sp - 1
       
      LET val_a = stack(sp)
      LET txt_a$ = stack_txt$(sp)
      LET orig_a = stack_orig(sp)
       
      IF t$ = "&" THEN
      ! Concatenazione valida SOLO se entrambi sono cifre iniziali singole non trasformate
         IF orig_a <> 1 OR orig_b <> 1 THEN EXIT SUB
          
         ! Unione algebrica delle due cifre: A * 10 + B
         LET res = val_a * 10 + val_b
         LET stack_txt$(sp) = STR$(res)
         ! Il numero a 2 cifre non è più concatenabile
         LET stack_orig(sp) = 0
          
      ELSE
         SELECT CASE t$
         CASE "+"
            LET res = val_a + val_b
         CASE "-"
            LET res = val_a - val_b
         CASE "*"
            LET res = val_a * val_b
         CASE "/"
         ! Divisione per zero non valida
            IF val_b = 0 THEN EXIT SUB
            LET res = val_a / val_b
         END SELECT
          
         LET stack_txt$(sp) = "(" & txt_a$ & " " & t$ & " " & txt_b$ & ")"
         ! Risultato calcolato: non più concatenabile
         LET stack_orig(sp) = 0
      END IF
       
      LET stack(sp) = res
       
   ELSE
   ! È un operando (numero normale, decimale o periodico)
      LET sp = sp + 1
      LET stack(sp) = tokens_val(i)
      LET stack_txt$(sp) = tokens_txt$(i)
      LET stack_orig(sp) = tokens_orig(i)
   END IF
NEXT i

! Salvataggio del risultato finale
IF sp = 1 THEN
   LET res_finale = stack(1)
    
   ! Verifica che res_finale sia un numero naturale (intero >= 0)
   IF res_finale >= 0 AND res_finale = INT(res_finale) THEN
      LET n = res_finale
      IF n <= 1000 THEN
      ! Salva solo la prima soluzione trovata (dando precedenza alle soluzioni di Fase 1)
         IF soluzione$(n) = "" THEN
            LET soluzione$(n) = stack_txt$(1)
         END IF
      END IF
   END IF
END IF
END SUB

...

Salvo errori & omissioni.

...


Pace e bene a tutti.

GfBo


Data creazione: Luglio 2026

Ultimo aggiornamento: Agosto 2026


Sito Web realizzato da Gianfranco Bo