Jerzy Ficowski (ten akowiec od "Dziś prawdziwych Cyganów już nie ma" "Jadą wozy kolorowe") 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.
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 algorytm:
#!/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.
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
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.
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.
Ponieważ ocenianie trójek "a + b = c" szło Gemini tak dobrze, więc przy niedzieli usiadłem i w kilku porcjach (bo lista jest długa) kazałem Gemini ocenić całą listę, wystawiając ocenę każdej trójce. Efekt zobaczycie tu: oto posortowany plik tylko z ocenami liczbowymi a oto posortowany plik również z ocenami opisowymi, w formacie jaki dał mi Gemini.
Dla przykładu zobaczcie, oto garść losowych trójek spośród tych ocenionych najwyżej, na 9:
ser + wuj = serwuj (9)
wita + mina = witamina (9)
całko + wita = całkowita (9)
powie + siła = powiesiła (9)
kop + ule = kopule (9)
macie + jem = maciejem (9)
ule + wami = ulewami (9)
niebie + gań = niebiegań (9)
pogard + zająca = pogardzająca (9)
bar + wami = barwami (9)
odra + żałoby = odrażałoby (9)
min + usa = minusa (9)
nauk + owiec = naukowiec (9)
lit + win = litwin (9)
mecze + tom = meczetom (9)
ud + zieleń = udzieleń (9)
nauk + owca = naukowca (9)
wykład + owca = wykładowca (9)
kur + sów = kursów (9)
budo + wami = budowami (9)
przesad + zając = przesadzając (9)
powie + sicie = powiesicie (9)
wierzy + ciel = wierzyciel (9)
krzyk + niemy = krzykniemy (9)
rząd + zimy = rządzimy (9)
skorzy + stali = skorzystali (9)
przyrząd + zając = przyrządzając (9)
ust + roi = ustroi (9)
alb + umów = albumów (9)
sto + sami = stosami (9)
ilu + zje = iluzje (9)
unie + ważni = unieważni (9)
towar + zyska = towarzyska (9)
se + zamów = sezamów (9)
kor + zenicie = korzenicie (9)
A oto trochę losowych trójek z dołu listy, ocenionych najniżej, na 0:
ok + ryłam = okryłam (0)
de + talem = detalem (0)
nade + szłam = nadeszłam (0)
ust + roiłybyście = ustroiłybyście (0)
śnie + żyłybyśmy = śnieżyłybyśmy (0)
niestaro + świecka = niestaroświecka (0)
słowo + twórczej = słowotwórczej (0)
ust + rojoną = ustrojoną (0)
udzie + lani = udzielani (0)
te + renami = terenami (0)
gen + etycznemu = genetycznemu (0)
nauk + owcami = naukowcami (0)
pocie + szyłby = pocieszyłby (0)
centy + metrów = centymetrów (0)
akt + orek = aktorek (0)
korzy + staniom = korzystaniom (0)
niezielono + górska = niezielonogórska (0)
wino + gronami = winogronami (0)
śnie + żyłbym = śnieżyłbym (0)
między + narodowym = międzynarodowym (0)
pełno + sprawny = pełnosprawny (0)
de + kretów = dekretów (0)
tego + roczny = tegoroczny (0)
grze + szyłeś = grzeszyłeś (0)
między + narodowa = międzynarodowa (0)
między + narodową = międzynarodową (0)
udzie + lałybyśmy = udzielałybyśmy (0)
pode + szłabym = podeszłabym (0)
poru + szyłoby = poruszyłoby (0)
ust + rojeniom = ustrojeniom (0)
zoo + logicznych = zoologicznych (0)
boga + tej = bogatej (0)
poru + szyliby = poruszyliby (0)
ok + ryłyby = okryłyby (0)
biało + ruskiemu = białoruskiemu (0)
meta + fizyce = metafizyce (0)
zło + żyłabym = złożyłabym (0)
gen + etyczna = genetyczna (0)
ok + ryciu = okryciu (0)
ok + ryłbym = okryłbym (0)
Czy to oceny słuszne czy niesłuszne - oceńcie sami. Ja mam wrażenie, że kiedy oglądam górę tej listy posortowanej po ocenach, to widzę więcej ciekawych trójek niż kiedy oglądałem przedtem listę nieocenioną.
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!