słowa takie że a+b=c

Jerzy Ficowski (ten akowiec od "Dziś prawdziwych Cyganów już nie ma") napisał kiedyś taki wiersz:

Pewien żarłok nienażarty
raz wygłodniał nie na żarty.
I wywiesił szyld na płocie,
że ochotę ma na płocie.
Tutaj na brak ryb narzeka,
bo daleko rybna rzeka.
Więc się zgłosił pewien żebrak
i rzekł żarłokowi, że brak:
płoci, karpi oraz śledzi,
ale rzeki pilnie śledzi,
i gdy tylko będzie w stanie,
to o świcie z łóżka wstanie,
po czym ruszy na Pomorze
i w zdobyciu ryb pomoże.
Odtąd żarłok nasz jedynie,
zamiast smacznych ryb je dynie.

a + b = c

Jak widać, jeden z manewrów, jaki stosuje, polega na tym, żeby znaleźć takie trójki polskich słów a, b, c, żeby a + b = c. Wziąłem więc przy sobocie moją fajną listę polskich słów i napisałem program, który szuka takich trójek (znaczy, ejajowi kazałem napisać, sam bym nie wymyślił takiego wydajnego algorytmu). Program na początku znajdował mnóstwo trójek trywialnych ("nie + ładnie = nieładnie") albo z użyciem słów tak egzotycznych, że aż dziwię się, że mam takie w mojej liście słów ("umie + rając = umierając"). Więc dodałem do programu jeszcze czarną listę słów, które ma unikać, i oto przedstawiam wam efekt.

Oto program, który szuka takich trójek. Patrzcie, całkiem zręczny kod:

#!/usr/bin/env python3
from collections import Counter

words = set(open('slowa.txt').read().splitlines())
blacklist = set(open('blacklist.txt').read().splitlines())
n = len(words)
found = 0
stats = Counter()
results = []

for idx, c in enumerate(words, 1):
    for i in range(1, len(c)):
        a, b = c[:i], c[i:]
        if a in words and b in words and a not in blacklist and b not in blacklist:
            results.append((a, b, c))
            found += 1
            stats[a] += 1
            stats[b] += 1
            stats[c] += 1
    if idx % 10000 == 0:
        print(f"\r{idx}/{n}  znaleziono: {found}", end='', flush=True)

with open('wyniki_posortowane_po_a.txt', 'w') as out:
    for a, b, c in sorted(results, key=lambda abc: abc[0]):
        out.write(f"{a} + {b} = {c}\n")

with open('wyniki_posortowane_po_b.txt', 'w') as out:
    for a, b, c in sorted(results, key=lambda abc: abc[1]):
        out.write(f"{a} + {b} = {c}\n")

print(f"\r{n}/{n} znaleziono: {found}")
print("Zapisano do wyniki_posortowane_po_a.txt i wyniki_posortowane_po_b.txt")

with open('statystyki.txt', 'w') as f:
    for word, count in stats.most_common():
        f.write(f"{word}\t{count}\n")

print(f"Statystyki ({len(stats)} słów) zapisano do statystyki.txt")

Oto wynik jego działania: lista posortowana po słowie a i lista posortowana po słowie b. Jest tam sporo niezbyt ciekawych trójek ("zielono + górski = zielonogórski"), ale są też całkiem ciekawe, na przykład:

dar + mowy = darmowy
de + batom = debatom
akt + orki = aktorki
błąd + zimy = błądzimy
bok + ser = bokser
brud + zimy = brudzimy
cent + rów = centrów
cent + rum = centrum
cię + żarówka = ciężarówka
cię + żar = ciężar
gry + źli = gryźli
kas + truje = kastruje
doda + tnie = dodatnie
wita + mina = witamina
zdrad + zając = zdradzając
trzy + mający = trzymający

Niektóre to klasyka ("wita + mina = witamina"), są takie, które sam kiedyś już odkryłem (o kimś skąpym a wygadanym można powiedzieć, że kiedy Bóg obdarzał ludzi wysyłanych na Ziemię do wcielenia się w noworodki talentami, to on stojąc w kolejce zobaczył okienko z tabliczką "dar mowy" i myślał, że tam coś za darmo będą dawać), a są i takie, które widzę pierwszy raz ("brud + zimy = brudzimy"). Fajnie. Można używać tego do układania różnych gier słów, na przykład że bywają takie debaty, kiedy po jednej stronie jest de, a po drugiej są baty.

a + b + c = d

Ficowski stosował też czwórki słów takie, żeby a + b + c = d, na przykład "nie + na + żarty = nienażarty". Akurat tej trójki ja nie znajdę, bo ja blacklistuję słowo "nie", ale ciekawie byłoby poszukać innych. Więc zmieniłem lekko ten program, żeby szukał takich czwórek - i oto efekt.

Oto program, który szuka takich czwórek. Oto wynik jego działania: lista posortowana po słowie a, lista posortowana po słowie b, lista posortowana po słowie c. Dosyć słabe poznajdował te czwórki, ale niektóre są ok, na przykład:

dwu + nas + tu = dwunastu
tak + sów + karze = taksówkarze

a + b = c + d

Spróbowałem też znaleźć takie czwórki, żeby a + b = c + d. Tu problem algorytmicznie okazał się trudniejszy (jak nie kombinować to O(n²), a u mnie n=250000), nawet ejaje nie dawały mu rady, dopiero Opus 4.6 napisał ten nietrywialny a ciekawy kod:

#!/usr/bin/env python3
from collections import defaultdict

words = set(open('slowa.txt').read().splitlines()) - set(open('blacklist.txt').read().splitlines())

print("Faza 1: buduję indeks środków...", flush=True)

# Dla każdego aktywnego środka m, zapamiętaj pary (b, d) takie że b = m+d, b in words, d in words
# middle_to_bd[m] = [(b, d), ...]
middle_to_bd = defaultdict(list)
for b in words:
    for i in range(1, len(b)):
        d = b[i:]
        if d in words:
            middle_to_bd[b[:i]].append((b, d))

print(f"  znaleziono {len(middle_to_bd)} aktywnych środków", flush=True)
print("Faza 2: szukam pełnych rekonstrukcji...", flush=True)

# Dla każdego słowa c, jeśli c[:i] (=a) in words i c[i:] (=m) ma wpisy w middle_to_bd,
# to a+b = c+d gdzie b = m+d
n = len(words)
count = 0
for idx, c in enumerate(words, 1):
    if idx % 10000 == 0:
        print(f"  postęp: {idx}/{n}", file=__import__('sys').stderr, flush=True)
    for i in range(1, len(c)):
        a = c[:i]
        m = c[i:]
        if a in words and m in middle_to_bd:
            for b, d in middle_to_bd[m]:
                print(f"{a}+{b} = {c}+{d}  ({a}{b})")
                count += 1

print(f"\nZnaleziono {count} rekonstrukcji.", file=__import__('sys').stderr)

Pełnego wyniku jego działania nie zamieszczam, bo znajduje takich czwórek całe gigabajty, do tego w większości nieciekawych. Na przykład:

wystawa+niespowodowana = wystawanie+spowodowana (wystawaniespowodowana)
szwagierko+wisiałeś = szwagierkowi+siałeś (szwagierkowisiałeś)
oddycha+niegrzejącą = oddychanie+grzejącą (oddychaniegrzejącą)
wyciąga+niekurcząca = wyciąganie+kurcząca (wyciąganiekurcząca)
rzecze+nieugryzieniach = rzeczenie+ugryzieniach (rzeczenieugryzieniach)
napisania+chwaliłybyście = napisaniach+waliłybyście (napisaniachwaliłybyście)
mali+narodziliśmy = malina+rodziliśmy (malinarodziliśmy)
wago+niekłuciu = wagonie+kłuciu (wagoniekłuciu)

A oto lista losowych tysiąca wyników tego programu.

czy AI umiałby odróżnić ciekawe kombinacje od nieciekawych?

Tak.

Przy takim automatycznym generowaniu gier słownych problemem jest często to, że program znajdzie dużo kombinacji, które formalnie spełniają wszelkie warunki, ale większość z nich jest nieciekawa. Spróbowałem więc, czy dałoby się użyć ejaja do odsiania ziarna od plew.

Użyłem takiego prompta:

Istnieją takie trójki słów a, b, c, że a+b=c. Na przykład "kot + lina = kotlina". Niektóre z nich są ciekawe, zaskakujące, zabawne - na przykład wspomniane "kot + lina = kotlina" czy "wita + mina = witamina". Ale są też nieciekawe, trywialne - na przykład "nie + ładny = nieładny" czy "zielono + górski = zielonogórski". Nieciekawe są te, gdzie mamy po prostu normalne słowotwórstwo (jak "nie + ładny = nieładny" czy "co + raz = coraz") albo kiedy użyte słowa są tak egzotyczne, że aż trzeba się pozastanawiać, czy to naprawdę jest legalne polskie słowo  (jak "de + tal = detal"). Dam ci teraz listę takich trójek. Dla każdej z nich daj mi najpierw ocenę opisową, a potem ocenę fajności w skali 0-9, gdzie 0 to trójka bardzo nieciekawa, a 9 to trójka bardzo ciekawa.
"""
kraj + obraz = krajobraz
włocha + tą = włochatą
gry + źli = gryźli
kas + truje = kastruje
prawdo + podobieństwo = prawdopodobieństwo
doda + tnie = dodatnie
tym + czasowy = tymczasowy
ok + rycia = okrycia
prawo + sławny = prawosławny
korzy + stałabym = korzystałabym
tam + tą = tamtą
tego + rocznego = tegorocznego
biało + ruski = białoruski
dar + mowy = darmowy
de + batom = debatom
akt + orki = aktorki
błąd + zimy = błądzimy
brud + zimy = brudzimy
wita + mina = witamina
"""

Dałem ten prompt Gemini 3 fast, a on wystawił takie oceny:

Trójka (a + b = c) Ocena opisowa Fajność (0-9)
kraj + obraz = krajobraz Klasyczna zbitka słowotwórcza. "Obraz kraju" to niemal dosłownie krajobraz. 1
włocha + tą = włochatą Zabawne przesunięcie akcentu i formy gramatycznej. 7
gry + źli = gryźli Bardzo mocna pozycja. Połączenie gry i emocji w drapieżnego ssaka. 9
kas + truje = kastruje "Kas" jest dość egzotyczne (jako skrót lub forma dopełniacza), co obniża ocenę. 3
prawdo + podobieństwo = prawdopodobieństwo Czysta, podręcznikowa złożeńcówka. Nuda lingwistyczna. 0
doda + tnie = dodatnie Ciekawe połączenie piosenkarki i czynności cięcia dające termin matematyczny. 8
tym + czasowy = tymczasowy Zbyt blisko naturalnego pochodzenia słowa (tym + czas). 1
ok + rycia = okrycia "Ok" to zapożyczenie, ale transformacja w "okrycie" jest nieoczywista. 5
prawo + sławny = prawosławny Dosłowne tłumaczenie z greckiego. Bardzo przewidywalne. 0
korzy + stałabym = korzystałabym "Korzy" nie jest samodzielnym słowem, to tylko podział morfologiczny. 2
tam + tą = tamtą Po prostu odmieniony zaimek. Bardzo trywialne. 0
tego + rocznego = tegorocznego Standardowe złożenie przymiotnikowe. Brak punktów za styl. 0
biało + ruski = białoruski Przymiotnik od nazwy państwa – czysta geografia. 0
dar + mowy = darmowy Genialne! Zupełna zmiana kontekstu z metafizycznego na materialny. 9
de + batom = debatom "De" to przedrostek, a "batom" brzmi egzotycznie. Średnio czytelne. 2
akt + orki = aktorki Bardzo dobre. Dokument prawny + orka rolnicza = zawód artystyczny. 8
błąd + zimy = błądzimy Poetyckie i zaskakujące przejście rzeczowników w czasownik. 8
brud + zimy = brudzimy Podobny, świetny mechanizm co w przypadku "błądzimy". 8
wita + mina = witamina Klasyk gatunku. Personifikacja miny, która wita. 7

Moim zdaniem bardzo dobrze ocenił, człowiek nie zrobiłby tego lepiej.

czy AI umiałby pisać jak Ficowski?

Nie.

Próbowałem prosić różne ejaje (używałem tylko darmowych), żeby napisały wiersz używając wybranych, ciekawych trójek a, b, c (wybranych i podanych przeze mnie). Wychodziło słabiutko. Na przykład Gemini 3 fast - chyba najlepszy z tych, co próbowałem - napisał tak:

Pechowa randka

Zabrał ją w miasto, bo był dość darmowy,
Miał gest szeroki, choć brakło mu dar mowy.
Weszli do baru, gdzie w mroku, przy centrum,
Kelner im podał stek krwawy – sam cent rum.

On chciał zaimponować, jak wielki bokser,
Lecz tylko sapał i pocił się, bok ser.
Potem milczeli, oddani debatom,
Ulegając błędom, nie zaś de batom.

Gdy wyszli na deszcz, rzekł: „Znów brudzimy,
W ten czas, gdy w mroku tak bez celu błąd zimy”.
Ona zaś, patrząc jak on wciąż kastruje
Wątek rozmowy, rzekła: „Nic, nie kas truje”.

Dobra nasza, ludzie!