LoginSignup
5
2

More than 5 years have passed since last update.

某サイトのアルゴリズムを作ってみる

Posted at

某サイトとは....

診断メーカー(おいしいパスタ作ったお前version)です

みなさんが一度は使用したことがあるのではと思います(偏見w
名前を入れると、勝手に診断結果をくれる便利(?)なものです

今回使ってみた診断は、文字からランダムに文章(?)を作ってくれます

診断したい名前:hmhm
診断結果:マジギレ一目惚れ女がお前家庭的な大貧民負けて大貧民負けて俺大貧民負けて作った

なんとなくの推測アルゴリズム

おいしいパスタ
作った
お前
家庭的な
女が
タイプの
俺
一目惚れ
大貧民負けて
マジギレ

の10個の文字列をランダムに出しているだけじゃないかと....

Coding

必要な変数定義

let array = ["おいしいパスタ", "作った", "お前", "家庭的な", "女が", "タイプの", "俺", "一目惚れ", "大貧民負けて", "マジギレ"]
var result: [String] = []
let output = 10
  • 使う文字を入れた配列 array を用意
  • 結果を入れる配列 result を用意
  • 本家ではおそらく10個の単語を出力してそうだったので、出力する数outputを10として用意

乱数で文字列を選択

for _ in 0..<output {
    let random = Int(arc4random_uniform(UInt32(array.count)))
    result.append(array[random])
}
print(result.reduce("", +))
  • ループを出力する数だけ実行
  • swiftで乱数を生成するとなるとarc4random_uniform()が良さげなので使用
  • 乱数の範囲は、0から使う文字を入れた配列 arrayの個数までに設定
  • 生成された乱数の場所の文字列をresultに格納
  • reduceで配列内の文字を合成

実行結果

func sindanmaker() {
    let array = ["おいしいパスタ", "作った", "お前", "家庭的な", "女が", "タイプの", "俺", "一目惚れ", "大貧民負けて", "マジギレ"]
    var result: [String] = []
    let output = 10
    for _ in 0..<output {
        let random = Int(arc4random_uniform(UInt32(array.count)))
        result.append(array[random])
    }
    print(result.reduce("", +))
}

sindanmaker()
// result print
お前マジギレマジギレマジギレ一目惚れお前お前マジギレ作ったタイプの

ふむふむ
とりあえず成功!
成功したので、これで終わりま....せん!w

問題点

arc4random()が毎回違う乱数を生成します
そのため実行するたびに結果が変わります

sindanmaker()
sindanmaker()

// result print
マジギレおいしいパスタ俺家庭的なおいしいパスタお前家庭的なおいしいパスタ一目惚れ大貧民負けて

おいしいパスタ作った家庭的な大貧民負けておいしいパスタ家庭的なおいしいパスタタイプのタイプの一目惚れ

普通はいいことなのですが、本家サイトでは、同じ名前を入れると同じ結果が返って来ます
本家に合わせるためにそこまでやってみます

Fixed coding

入力された値からhash値をとる

いろいろ考えた末、入力された文字列からhash値を得て、それを元に診断結果を作ろうかなと

let str: String // 入力値
var hash = str.hashValue

swiftに元からあるメソッドを使っていきます
これでString型をInt型にすることができました

example.swift
let str: String = "hmhm"
var hash = str.hashValue // 4799450094189775535

このように入力された文字によって固有の値が出て来ます

hash値の変換

let num = Int(log10(Double(hash)) + 1)
if num < 10 {
    return
}

hash値の桁数を取得します

  • log10()メソッドで10の何乗かを取得
  • 桁数はそれに+1した数になります
  • それをIntで整数化します(少数第一位のくり上がりを気にする場合は、ここでfloorなどでで切り捨てすべきでしたね...)
let round = Int(powf(10, Float(num - 10)))
let prefix = hash / round
let suffix = hash - prefix * round
var value = prefix + suffix

ここが一番悩みました...
本家サイトがどうやっているのかわからないのですが、自分流でw

  • hash値の桁数から出力分の10桁を引いた数分、10をべき乗powfしていきます
  • hash値の桁数を上から10桁分の数値におとしますprefix
  • hash値の上から10桁を抜いた数を取得しますsuffix
  • それを足します(ここは自己流ですw簡単に10桁にするためにこれでよいかなと....)
example.swift
let str: String = "hmhm"
var hash = str.hashValue // 4799450094189775535

let round = Int(powf(10, Float(num - 10))) // 1000000000
let prefix = hash / round // 4799450094
let suffix = hash - prefix * round // 189775535
var value = prefix + suffix // 4989225629

とりあえず、10桁の数値ができました

変数定義

let array = ["おいしいパスタ", "作った", "お前", "家庭的な", "女が", "タイプの", "俺", "一目惚れ", "大貧民負けて", "マジギレ"]
var result: [String] = []
let output = 10

先ほどと同じです

文字列を選択

for _ in 0..<output {
    let random = value % 10
    value = value / 10
    result.append(array[random])
}
print(result.reduce("", +))

先ほどhash値から求めた10桁の数字を使います

  • 10桁の数字を10で割った余りを計算(1の位の値を取得)
  • 10桁の数字を10で割る(10の位の数値を1の位にするため)
  • 結果の配列に足していく
example.swift
var value = prefix + suffix // 4989225629
for _ in 0..<output {
    let random = value % 10
    print("random: \(random)")
    value = value / 10
    print("value: \(value)")
    result.append(array[random])
}

// print
random: 9
value: 498922562

random: 2
value: 49892256

random: 6
value: 4989225

random: 5
value: 498922

random: 2
value: 49892

random: 2
value: 4989

random: 9
value: 498

random: 8
value: 49

random: 9
value: 4

random: 4
value: 0

10桁だった数字が減っていくのと、randomに1の位が代入されているのがわかると思います
randomに入った数値の場所を、文字列の配列arrayから取り出していきます

実行結果

func sindanmaker_fixed(str: String) {
    var hash = str.hashValue
    let num = Int(log10(Double(hash)) + 1)
    if num < 10 {
        return
    }

    let round = Int(powf(10, Float(num - 10)))
    let prefix = hash / round
    let suffix = hash - prefix * round
    var value = prefix + suffix

    let array = ["おいしいパスタ", "作った", "お前", "家庭的な", "女が", "タイプの", "俺", "一目惚れ", "大貧民負けて", "マジギレ"]
    var result: [String] = []
    let output = 10

    for _ in 0..<output {
        let random = value % 10
        value = value / 10
        result.append(array[random])
    }
    print(result.reduce("", +))
}

sindanmaker_fixed(str: "hmhm")

// result print
マジギレお前俺タイプのお前お前マジギレ大貧民負けてマジギレ女が

とりあえず診断結果は出てる!
肝心なのはもう一度同じ名前でやって同じ結果になるか...

sindanmaker_fixed(str: "hmhm")
sindanmaker_fixed(str: "hmhm")

// result print
マジギレお前俺タイプのお前お前マジギレ大貧民負けてマジギレ女が

マジギレお前俺タイプのお前お前マジギレ大貧民負けてマジギレ女が

やったー!
違うのもやってみよう

sindanmaker_fixed(str: "hmhmsh")

// result print
一目惚れ大貧民負けて作った大貧民負けてお前大貧民負けて作った俺大貧民負けて女が

大丈夫そう!
ということでこれで終われる!

最後に

ながながと書いてしまいましたが、読んでいただきありがとうございます
書いてる途中で、「ここは判定が足りなかったorz」などとなりましたが...
とりあえず、(自己流ですが)某サイトのようなアルゴリズムを作れたのではないかなと:relaxed:

5
2
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
5
2