0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

発音可能な無意味カナ文字列を生成する

0
Last updated at Posted at 2025-10-05

はじめに

発音可能な無意味カナ文字列を作る方法を検討しました。

課題

無意味(ランダム)なカナ文字列であればカナのリストを作ってrandom.choiceすれば作れますが、小文字カナがあちこちに出てきたり、「ン」「ッ」「ー」が連続したりすると、発音に適さない文字列ができる可能性があります。

案1

カナのリストではなくモウラのリストを並べるようにすれば、小文字カナが不自然に登場することはなくなりそうです。

jamorasepのkanamapから「ヅ」と「ズ」など発音が重複するものを除いたモウラのリストを作ります。

mora.csv
katakana,voiced,yoon,foreign,kunrei,hepburn,simple-ipa
ア,有声,,,a,a,a
イ,有声,,,i,i,i
ウ,有声,,,u,u,u
エ,有声,,,e,e,e
オ,有声,,,o,o,o
カ,,,,ka,ka,ka
キ,,,,ki,ki,kji
ク,,,,ku,ku,ku
ケ,,,,ke,ke,ke
コ,,,,ko,ko,ko
ガ,濁,,,ga,ga,ga
ギ,濁,,,gi,gi,gji
グ,濁,,,gu,gu,gu
ゲ,濁,,,ge,ge,ge
ゴ,濁,,,go,go,go
キャ,,拗,,kya,kya,kja
キュ,,拗,,kyu,kyu,kju
キェ,,拗,True,kye,kye,kje
キョ,,拗,,kyo,kyo,kjo
ギャ,濁,拗,,gya,gya,gja
ギュ,濁,拗,,gyu,gyu,gju
ギェ,濁,拗,True,gye,gye,gje
ギョ,濁,拗,,gyo,gyo,gjo
クァ,,合拗,True,kua,kwa,kwa
クィ,,合拗,True,kui,kwi,kwi
クェ,,合拗,True,kue,kwe,kwe
クォ,,合拗,True,kuo,kwo,kwo
グァ,濁,合拗,True,gua,gwa,gwa
グィ,濁,合拗,True,gui,gwi,gwi
グェ,濁,合拗,True,gue,gwe,gwe
グォ,濁,合拗,True,guo,gwo,gwo
サ,,,,sa,sa,sa
シ,,,,si,shi,ɕi
ス,,,,su,su,su
セ,,,,se,se,se
ソ,,,,so,so,so
ザ,濁,,,za,za,za
ジ,濁,,,zi,ji,ʒi
ズ,濁,,,zu,zu,zu
ゼ,濁,,,ze,ze,ze
ゾ,濁,,,zo,zo,zo
スィ,,,True,tui,si,si
ズィ,濁,,True,zui,zi,zi
シャ,,拗,,sya,sha,ɕa
シュ,,拗,,syu,shu,ɕu
シェ,,拗,True,sye,she,ɕe
ショ,,拗,,syo,sho,ɕo
ジャ,濁,拗,,zya,ja,ʒa
ジュ,濁,拗,,zyu,ju,ʒu
ジェ,濁,拗,True,zye,je,ʒe
ジョ,濁,拗,,zyo,jo,ʒo
タ,,,,ta,ta,ta
チ,,,,ti,chi,tɕi
ツ,,,,tu,tsu,tsu
テ,,,,te,te,te
ト,,,,to,to,to
ダ,濁,,,da,da,da
デ,濁,,,de,de,de
ド,濁,,,do,do,do
トゥ,,,True,tou,tu,tu
ドゥ,濁,,True,dou,du,du
チャ,,拗,,tya,cha,tɕa
チュ,,拗,,tyu,chu,tɕu
チェ,,拗,True,tye,che,tɕe
チョ,,拗,,tyo,cho,tɕo
テャ,,拗,True,teya,tya,tja
ティ,,,True,tei,ti,tji
テュ,,拗,True,teyu,tyu,tju
テェ,,拗,True,tee,tye,tje
テョ,,拗,True,teyo,tyo,tjo
デャ,濁,拗,True,deya,dya,dja
ディ,濁,,True,dei,di,dji
デュ,濁,拗,True,deyu,dyu,dju
デェ,濁,拗,True,dee,dye,dje
デョ,濁,拗,True,deyo,dyo,djo
ツァ,,,True,tua,tsa,tsa
ツィ,,,True,tui,tsi,tsi
ツェ,,,True,tue,tse,tse
ツォ,,,True,tuo,tso,tso
ツャ,,拗,True,tuya,tsya,tsja
ツュ,,拗,True,tuyu,tsyu,tsju
ツョ,,拗,True,tuyo,tsyo,tsjo
ナ,有声,,,na,na,na
ニ,有声,,,ni,ni,nji
ヌ,有声,,,nu,nu,nu
ネ,有声,,,ne,ne,ne
ノ,有声,,,no,no,no
ニャ,有声,拗,,nya,nya,nja
ニュ,有声,拗,,nyu,nyu,nju
ニェ,有声,拗,True,nye,nye,nje
ニョ,有声,拗,,nyo,nyo,njo
ハ,,,,ha,ha,ha
ヒ,,,,hi,hi,çi
フ,,,,hu,fu,ɸu
ヘ,,,,he,he,he
ホ,,,,ho,ho,ho
バ,濁,,,ba,ba,ba
ビ,濁,,,bi,bi,bji
ブ,濁,,,bu,bu,bu
ベ,濁,,,be,be,be
ボ,濁,,,bo,bo,bo
パ,半濁,,,pa,pa,pa
ピ,半濁,,,pi,pi,pji
プ,半濁,,,pu,pu,pu
ペ,半濁,,,pe,pe,pe
ポ,半濁,,,po,po,po
ヒャ,,拗,,hya,hya,ça
ヒュ,,拗,,hyu,hyu,çu
ヒェ,,拗,True,hye,hye,çe
ヒョ,,拗,,hyo,hyo,ço
ビャ,濁,拗,,bya,bya,bja
ビュ,濁,拗,,byu,byu,bju
ビェ,濁,拗,True,bye,bye,bje
ビョ,濁,拗,,byo,byo,bjo
ピャ,半濁,拗,,pya,pya,pja
ピュ,半濁,拗,,pyu,pyu,pju
ピェ,半濁,拗,True,pye,pye,pje
ピョ,半濁,拗,,pyo,pyo,pjo
ファ,,,True,hua,fa,ɸa
フィ,,,True,hui,fi,ɸi
フェ,,,True,hue,fe,ɸe
フォ,,,True,huo,fo,ɸo
フャ,,拗,True,huya,fya,ɸja
フュ,,拗,True,huyu,fyu,ɸju
フョ,,拗,True,huyo,fyo,ɸjo
マ,有声,,,ma,ma,ma
ミ,有声,,,mi,mi,mji
ム,有声,,,mu,mu,mu
メ,有声,,,me,me,me
モ,有声,,,mo,mo,mo
ミャ,有声,拗,,mya,mya,mja
ミュ,有声,拗,,myu,myu,mju
ミェ,有声,拗,True,mye,mye,mje
ミョ,有声,拗,,myo,myo,mjo
ヤ,有声,拗,,ya,ya,ja
ユ,有声,拗,,yu,yu,ju
ヨ,有声,拗,,yo,yo,jo
イェ,,拗,True,ye,ye,je
ラ,有声,,,ra,ra,ra
リ,有声,,,ri,ri,rji
ル,有声,,,ru,ru,ru
レ,有声,,,re,re,re
ロ,有声,,,ro,ro,ro
リャ,有声,拗,,rya,rya,rja
リュ,有声,拗,,ryu,ryu,rju
リェ,有声,拗,True,rye,rye,rje
リョ,有声,拗,,ryo,ryo,rjo
ワ,有声,,,wa,wa,wa
ウィ,有声,,True,ui,wi,wi
ウェ,有声,,True,ue,we,we
ウォ,有声,,True,uo,wo,wo
ヴァ,濁,,True,ba,va,va
ヴィ,濁,,True,bi,vi,vi
ヴ,濁,,True,bu,vu,vu
ヴェ,濁,,True,be,ve,ve
ヴォ,濁,,True,bo,vo,vo
ヴャ,濁,拗,True,buya,vya,vja
ヴュ,濁,拗,True,buyu,vyu,vju
ヴョ,濁,拗,True,buyo,vyo,vjo
ン,有声,撥音,,n,n,N
ッ,,促音,,Q,Q,Q
ー,有声,長音,,:,:,:

mora.csvをロードして、katakana列からランダムに指定文字数を取り出して並べる関数を作ります。

import pandas as pd
import random

def load_katakana_column(csv_path: str) -> list[str]:
  """
  Load mora.csv and return the 'katakana' column as a list.

  Args:
    csv_path (str): Path to mora.csv

  Returns:
    list[str]: List of katakana moras

  """
  df = pd.read_csv(csv_path)
  katakana_list = df["katakana"].dropna().tolist()
  return katakana_list

katakana_list = load_katakana_column("mora.csv")

「ン」「ー」「ッ」が最初に来ず、連続もしない、という制約をいれています。連続については「ンーッ」など連続しても不自然じゃないケースもあるのですが、細かい分岐を考えるのは手間なので、安全寄りの実装で妥協します。

def random_mora_sequence(length: int) -> str:
  """
  Generate a random sequence of moras of specified length.

  Args:
    length (int): Number of moras in the sequence

  Returns:
    str: Concatenated katakana sequence

  Raises:
    ValueError: If length is not positive
  """
  sequence = ""
  for i in range(length):
    if i == 0:
      first_kana = [k for k in katakana_list if k not in "ンーッ"]
      sequence += random.choice(first_kana)
      continue
    prev = sequence[-1]
    if prev in "ンーッ":
      sequence += random.choice(list(set(katakana_list) - {"", "", ""}))
      continue
    sequence += random.choice(katakana_list)
  return sequence
random_mora_sequence(32)

何回か実行すると以下のような文字列が得られます。

  • ヴィベグォビュニョニホフリョビャデャズネテャキーファフョヴデュグヒェクピャデョデュワグァチベテョフョ
  • ミャジェエミョクォプナヴャテョグィリャミャヒェシェジュズィミュヤフャボチェノビェゲヘジュウォヒキュッザビョ

「ヴィ」「グォ」など2文字モウラの出現頻度がやたらと高い印象があります。
モウラのユニークなリストには2文字モウラが1文字モウラよりも多く登場するためこのようになるのだと思います。

案2

2文字モウラの出現率を抑える方法を考えます。
「ッ」以外の小文字カナが登場できるのは特定の大文字カナの直後のみですから、その対応マップを作って、特定の大文字カナの直後にのみ小文字カナが現れうるようにします。

まず大文字カナと直後に登場可能な小文字カナのマップを作ります。

from collections import defaultdict

# 1文字大文字カナ(例: ア, イ, ...)と直後に来られる小文字(例: ャ, ュ, ...)の辞書を作成

# 小文字カナのリスト(代表例)
small_kana = {"", "", "", "", "", "", "", "", ""}

_kana_dict: dict[str, set[str]] = defaultdict(set)

for kana in katakana_list:
  # 1文字目が大文字カナ、2文字目が小文字カナの場合のみ
  if len(kana) == 2 and kana[1] in small_kana:
    _kana_dict[kana[0]].add(kana[1])

# 辞書を通常のdictに変換
kana_dict = {k: sorted(list(v)) for k, v in _kana_dict.items()}
kana_dict
{'キ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ギ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ク': ['ァ', 'ィ', 'ェ', 'ォ'],
 'グ': ['ァ', 'ィ', 'ェ', 'ォ'],
 'ス': ['ィ'],
 'ズ': ['ィ'],
 'シ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ジ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ト': ['ゥ'],
 'ド': ['ゥ'],
 'チ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'テ': ['ィ', 'ェ', 'ャ', 'ュ', 'ョ'],
 'デ': ['ィ', 'ェ', 'ャ', 'ュ', 'ョ'],
 'ツ': ['ァ', 'ィ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ'],
 'ニ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ヒ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ビ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ピ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'フ': ['ァ', 'ィ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ'],
 'ミ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'イ': ['ェ'],
 'リ': ['ェ', 'ャ', 'ュ', 'ョ'],
 'ウ': ['ィ', 'ェ', 'ォ'],
 'ヴ': ['ァ', 'ィ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ']}

作成したマップに基づきカナ文字列を生成します。

def random_kana_sequence(length: int) -> str:
  """
  Generate a random katakana sequence:
  - First character: large kana only
  - Subsequent characters: can be large or small kana (using kana_dict for valid combinations)

  Args:
    length (int): Length of the sequence

  Returns:
    str: Generated katakana sequence

  Raises:
    ValueError: If length is not positive
  """
  if length <= 0:
    raise ValueError(f"Expected positive integer, got {length}")

  # 1文字目: 大文字カナのみ
  large_first_kana = [k for k in katakana_list if len(k) == 1 and k not in "ンーッ"]
  special_kana = ["", "", ""]

  first = random.choice(large_first_kana)

  result = [first]
  for _ in range(length - 1):
    prev = result[-1]
    next_kana_candidates = large_first_kana + kana_dict.get(prev, [])
    if prev not in special_kana:
      next_kana_candidates += special_kana
    next_kana = random.choice(next_kana_candidates)
    result.append(next_kana)
  sequence = "".join(result)
  return sequence

# 例: 32文字のランダムカナ文字列
random_kana_sequence(32)

何度か実行すると以下のような結果が得られます。

  • シミゼデヘネヘガウヤポミリイヤウホゾザルッスピレヴブスクタベブグ
  • イパクチルロブピネツヤフエケマシボゴリョヒャボボパソムゴネポナシ
  • チントデレミラオヒモアコウフソセオリヌヨリェロペクバエラカスノケ

2文字モウラの出現は抑えられています。ただ逆に抑えられ過ぎかもしれません。

おわりに

発音可能な無意味カナ文字列を生成する方法を2通り考えました。
一長一短ですが、二文字モウラが少ない案2のほうが少し好みな気はしました。
より日本語らしさを目指すなら、現実の文章の出現率と合わせて、各モウラの割合を変えても良いかもしれません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?