「恋の連立方程式」をPythonでシミュレーションしてみた

はじめに:愛情も数式で表現できる?

恋愛は感情的なものだと思われがちですが、実は数学的にモデル化できる側面があります。最近のAIマッチングアプリの躍進を見ても、データサイエンスと機械学習が恋愛の世界で大きな成果を上げていることは明らかです。

今回は「恋の連立方程式」という興味深いコンセプトをPythonで実装し、AIと数学を駆使して恋愛のメカニズムをシミュレーションしてみました。この記事では、初心者でも理解できるよう丁寧に解説しながら、実用的なコードも紹介します。

恋の連立方程式とは?

「恋の連立方程式」とは、二人以上の人間関係における感情の相互作用を数式で表現したものです。これは以下のような要素を含みます:

  • 相互の好感度:AさんのBさんに対する好感度とBさんのAさんに対する好感度
  • 時間の経過による変化:関係性の発展や冷却期間の影響
  • 外部要因:共通の趣味、価値観の一致度、物理的距離など

数学的には、これらを連立方程式として表現できます:

dA/dt = f(A, B, 外部要因)
dB/dt = g(A, B, 外部要因)

ここで、AとBはそれぞれの好感度、tは時間を表します。

Python環境の準備

まずは必要なライブラリをインストールしましょう。

pip install numpy matplotlib pandas sympy scikit-learn

基本的な恋愛マッチングモデルの実装

1. 単純な好感度シミュレーター

最初に、シンプルな好感度の変化をシミュレーションしてみます:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.integrate import odeint
import random

class LoveEquationSimulator:
    def __init__(self, initial_feelings_a=0.5, initial_feelings_b=0.5):
        self.feelings_a = initial_feelings_a  # Aの好感度
        self.feelings_b = initial_feelings_b  # Bの好感度
        
    def love_dynamics(self, y, t, params):
        """恋愛力学の微分方程式"""
        a, b = y
        alpha, beta, gamma, delta = params
        
        # 相互作用を考慮した好感度変化
        da_dt = alpha * b - beta * a  # Bの好感度がAに影響
        db_dt = gamma * a - delta * b  # Aの好感度がBに影響
        
        return [da_dt, db_dt]
    
    def simulate(self, days=100, params=(0.1, 0.05, 0.08, 0.04)):
        """シミュレーション実行"""
        t = np.linspace(0, days, days)
        initial_conditions = [self.feelings_a, self.feelings_b]
        
        solution = odeint(self.love_dynamics, initial_conditions, t, args=(params,))
        
        return t, solution[:, 0], solution[:, 1]

# シミュレーション実行
simulator = LoveEquationSimulator(0.3, 0.7)
time, feelings_a, feelings_b = simulator.simulate()

# 結果の可視化
plt.figure(figsize=(12, 6))
plt.plot(time, feelings_a, label='Person A', linewidth=2)
plt.plot(time, feelings_b, label='Person B', linewidth=2)
plt.xlabel('Time (days)')
plt.ylabel('Feelings Level')
plt.title('Love Equation Simulation')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

2. 外部要因を考慮した高度なモデル

次に、より現実的な要因を組み込んだモデルを作成します:

class AdvancedLoveSimulator:
    def __init__(self):
        self.compatibility_factors = {
            'shared_interests': 0.8,
            'value_alignment': 0.9,
            'physical_attraction': 0.7,
            'communication_style': 0.6,
            'life_goals': 0.85
        }
        
    def calculate_compatibility_score(self, person_a_profile, person_b_profile):
        """相性スコアの計算"""
        total_score = 0
        for factor, weight in self.compatibility_factors.items():
            if factor in person_a_profile and factor in person_b_profile:
                # 類似度計算(0-1の値)
                similarity = 1 - abs(person_a_profile[factor] - person_b_profile[factor])
                total_score += similarity * weight
        
        return total_score / len(self.compatibility_factors)
    
    def enhanced_love_dynamics(self, y, t, compatibility_score, external_events):
        """拡張された恋愛力学"""
        a, b = y
        
        # 基本的な相互作用係数(相性に基づく)
        alpha = 0.1 * compatibility_score
        beta = 0.05
        gamma = 0.08 * compatibility_score
        delta = 0.04
        
        # 外部イベントの影響
        event_impact = self.get_event_impact(t, external_events)
        
        da_dt = alpha * b - beta * a + event_impact
        db_dt = gamma * a - delta * b + event_impact
        
        # 感情の範囲を0-1に制限
        da_dt = max(-a, min(1-a, da_dt))
        db_dt = max(-b, min(1-b, db_dt))
        
        return [da_dt, db_dt]
    
    def get_event_impact(self, t, events):
        """特定時点での外部イベントの影響"""
        impact = 0
        for event_time, event_strength in events:
            if abs(t - event_time) < 1:  # イベント発生日前後の影響
                impact += event_strength * np.exp(-abs(t - event_time))
        return impact
    
    def run_simulation(self, person_a, person_b, days=100):
        """高度なシミュレーション実行"""
        compatibility = self.calculate_compatibility_score(person_a, person_b)
        
        # ランダムな外部イベント生成
        external_events = [(random.randint(10, 90), random.uniform(-0.1, 0.2)) 
                          for _ in range(5)]
        
        t = np.linspace(0, days, days)
        initial_conditions = [0.5, 0.5]
        
        solution = odeint(self.enhanced_love_dynamics, initial_conditions, t, 
                         args=(compatibility, external_events))
        
        return t, solution[:, 0], solution[:, 1], compatibility, external_events

# サンプル人物プロファイル
person_a = {
    'shared_interests': 0.8,
    'value_alignment': 0.7,
    'physical_attraction': 0.6,
    'communication_style': 0.9,
    'life_goals': 0.8
}

person_b = {
    'shared_interests': 0.9,
    'value_alignment': 0.8,
    'physical_attraction': 0.7,
    'communication_style': 0.8,
    'life_goals': 0.9
}

# 高度なシミュレーション実行
advanced_sim = AdvancedLoveSimulator()
time, feelings_a, feelings_b, compatibility, events = advanced_sim.run_simulation(person_a, person_b)

print(f"相性スコア: {compatibility:.3f}")
print(f"外部イベント: {events}")

AIマッチングアルゴリズムの実装

機械学習を使った現代的なマッチングアルゴリズムも実装してみましょう:

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import pandas as pd

class AIMatchingPredictor:
    def __init__(self):
        self.model = RandomForestRegressor(n_estimators=100, random_state=42)
        self.feature_columns = []
        
    def generate_training_data(self, n_samples=1000):
        """トレーニング用データの生成"""
        data = []
        
        for _ in range(n_samples):
            # 個人特性の生成
            person_a_features = {
                'age_a': random.randint(20, 45),
                'education_a': random.randint(1, 5),
                'income_a': random.randint(3, 10),
                'extroversion_a': random.uniform(0, 1),
                'openness_a': random.uniform(0, 1)
            }
            
            person_b_features = {
                'age_b': random.randint(20, 45),
                'education_b': random.randint(1, 5),
                'income_b': random.randint(3, 10),
                'extroversion_b': random.uniform(0, 1),
                'openness_b': random.uniform(0, 1)
            }
            
            # 派生特徴量
            derived_features = {
                'age_diff': abs(person_a_features['age_a'] - person_b_features['age_b']),
                'education_diff': abs(person_a_features['education_a'] - person_b_features['education_b']),
                'income_diff': abs(person_a_features['income_a'] - person_b_features['income_b']),
                'personality_compatibility': 1 - abs(person_a_features['extroversion_a'] - person_b_features['extroversion_b'])
            }
            
            # 全特徴量を結合
            features = {**person_a_features, **person_b_features, **derived_features}
            
            # マッチング成功率の計算(実際のロジック)
            success_rate = self.calculate_success_rate(features)
            
            features['match_success'] = success_rate
            data.append(features)
        
        return pd.DataFrame(data)
    
    def calculate_success_rate(self, features):
        """実際のマッチング成功率の計算"""
        success = 0.5  # ベースライン
        
        # 年齢差の影響
        success -= min(features['age_diff'] * 0.02, 0.3)
        
        # 学歴差の影響
        success -= min(features['education_diff'] * 0.05, 0.2)
        
        # 性格の相性
        success += features['personality_compatibility'] * 0.3
        
        # 収入差の影響(適度な差は好ましい)
        income_diff = features['income_diff']
        if income_diff < 2:
            success += 0.1
        elif income_diff > 5:
            success -= 0.2
        
        return max(0, min(1, success))
    
    def train_model(self, data):
        """モデルの訓練"""
        self.feature_columns = [col for col in data.columns if col != 'match_success']
        X = data[self.feature_columns]
        y = data['match_success']
        
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
        
        self.model.fit(X_train, y_train)
        
        # モデル評価
        y_pred = self.model.predict(X_test)
        mse = mean_squared_error(y_test, y_pred)
        
        return mse, X_test, y_test, y_pred
    
    def predict_compatibility(self, person_a, person_b):
        """互換性の予測"""
        features = {**person_a, **person_b}
        
        # 派生特徴量の計算
        features['age_diff'] = abs(person_a['age_a'] - person_b['age_b'])
        features['education_diff'] = abs(person_a['education_a'] - person_b['education_b'])
        features['income_diff'] = abs(person_a['income_a'] - person_b['income_b'])
        features['personality_compatibility'] = 1 - abs(person_a['extroversion_a'] - person_b['extroversion_b'])
        
        # DataFrame形式に変換
        feature_df = pd.DataFrame([features])[self.feature_columns]
        
        return self.model.predict(feature_df)[0]

# AIマッチング予測器の使用例
ai_matcher = AIMatchingPredictor()

# トレーニングデータの生成と学習
training_data = ai_matcher.generate_training_data(5000)
mse, X_test, y_test, y_pred = ai_matcher.train_model(training_data)

print(f"モデルの予測精度 (MSE): {mse:.4f}")

# 実際の予測例
test_person_a = {
    'age_a': 28,
    'education_a': 4,
    'income_a': 6,
    'extroversion_a': 0.7,
    'openness_a': 0.8
}

test_person_b = {
    'age_b': 26,
    'education_b': 4,
    'income_b': 5,
    'extroversion_b': 0.6,
    'openness_b': 0.9
}

compatibility_score = ai_matcher.predict_compatibility(test_person_a, test_person_b)
print(f"予測されたマッチング成功率: {compatibility_score:.3f}")

最適化アルゴリズムによるマッチング

SympyとNumPyを使って、最適なマッチングを数学的に求めてみましょう:

import sympy as sp
from scipy.optimize import minimize
import itertools

class OptimalMatchingCalculator:
    def __init__(self):
        self.people_profiles = []
        
    def add_person(self, profile):
        """人物プロファイルの追加"""
        self.people_profiles.append(profile)
    
    def calculate_pair_score(self, person1, person2):
        """ペアの相性スコア計算"""
        score = 0
        weights = {
            'age': 0.2,
            'interests': 0.3,
            'values': 0.3,
            'personality': 0.2
        }
        
        for attribute, weight in weights.items():
            if attribute in person1 and attribute in person2:
                similarity = 1 - abs(person1[attribute] - person2[attribute])
                score += similarity * weight
        
        return score
    
    def find_optimal_matching(self):
        """最適マッチングの探索"""
        n_people = len(self.people_profiles)
        if n_people % 2 != 0:
            raise ValueError("参加者数は偶数である必要があります")
        
        best_score = -1
        best_matching = None
        
        # 全てのペアリング組み合わせを試行
        people_indices = list(range(n_people))
        
        for matching in self.generate_all_matchings(people_indices):
            total_score = 0
            for i in range(0, len(matching), 2):
                person1 = self.people_profiles[matching[i]]
                person2 = self.people_profiles[matching[i+1]]
                total_score += self.calculate_pair_score(person1, person2)
            
            if total_score > best_score:
                best_score = total_score
                best_matching = matching
        
        return best_matching, best_score
    
    def generate_all_matchings(self, people):
        """全てのマッチング組み合わせを生成"""
        if len(people) == 0:
            yield []
        elif len(people) == 2:
            yield people
        else:
            first = people[0]
            for i in range(1, len(people)):
                partner = people[i]
                remaining = people[1:i] + people[i+1:]
                for rest in self.generate_all_matchings(remaining):
                    yield [first, partner] + rest

# 最適マッチング計算機の使用例
matcher = OptimalMatchingCalculator()

# サンプル人物データ
sample_people = [
    {'name': 'Alice', 'age': 0.5, 'interests': 0.8, 'values': 0.7, 'personality': 0.6},
    {'name': 'Bob', 'age': 0.6, 'interests': 0.7, 'values': 0.8, 'personality': 0.7},
    {'name': 'Charlie', 'age': 0.4, 'interests': 0.9, 'values': 0.6, 'personality': 0.8},
    {'name': 'Diana', 'age': 0.3, 'interests': 0.8, 'values': 0.9, 'personality': 0.5}
]

for person in sample_people:
    matcher.add_person(person)

optimal_pairs, score = matcher.find_optimal_matching()
print(f"最適マッチング: {optimal_pairs}")
print(f"総合スコア: {score:.3f}")

# ペアの詳細表示
for i in range(0, len(optimal_pairs), 2):
    person1 = sample_people[optimal_pairs[i]]
    person2 = sample_people[optimal_pairs[i+1]]
    pair_score = matcher.calculate_pair_score(person1, person2)
    print(f"{person1['name']} & {person2['name']}: {pair_score:.3f}")

実用的な応用例:デート最適化システム

最後に、より実用的な「デート最適化システム」を作成してみましょう:

class DateOptimizationSystem:
    def __init__(self):
        self.couples = []
        self.date_activities = [
            {'name': 'Movie', 'cost': 20, 'intimacy': 0.6, 'excitement': 0.7},
            {'name': 'Restaurant', 'cost': 50, 'intimacy': 0.8, 'excitement': 0.5},
            {'name': 'Park Walk', 'cost': 0, 'intimacy': 0.7, 'excitement': 0.4},
            {'name': 'Concert', 'cost': 80, 'intimacy': 0.5, 'excitement': 0.9},
            {'name': 'Museum', 'cost': 15, 'intimacy': 0.6, 'excitement': 0.6},
            {'name': 'Cooking Class', 'cost': 40, 'intimacy': 0.9, 'excitement': 0.8}
        ]
    
    def add_couple(self, person_a, person_b, budget, preferences):
        """カップルの追加"""
        couple = {
            'person_a': person_a,
            'person_b': person_b,
            'budget': budget,
            'preferences': preferences,
            'relationship_score': 0.5
        }
        self.couples.append(couple)
    
    def calculate_activity_score(self, couple, activity):
        """活動スコアの計算"""
        if activity['cost'] > couple['budget']:
            return 0
        
        # 好みに基づくスコア計算
        prefs = couple['preferences']
        score = (
            activity['intimacy'] * prefs.get('intimacy_preference', 0.5) +
            activity['excitement'] * prefs.get('excitement_preference', 0.5) +
            (1 - activity['cost'] / 100) * prefs.get('budget_consciousness', 0.5)
        ) / 3
        
        return score
    
    def recommend_date(self, couple_index):
        """デート推奨の生成"""
        couple = self.couples[couple_index]
        
        activity_scores = []
        for activity in self.date_activities:
            score = self.calculate_activity_score(couple, activity)
            activity_scores.append((activity, score))
        
        # スコア順でソート
        activity_scores.sort(key=lambda x: x[1], reverse=True)
        
        return activity_scores[:3]  # トップ3を返す
    
    def simulate_date_outcome(self, couple_index, activity):
        """デート結果のシミュレーション"""
        couple = self.couples[couple_index]
        base_score = self.calculate_activity_score(couple, activity)
        
        # ランダム要素を加える
        random_factor = random.uniform(0.8, 1.2)
        final_score = base_score * random_factor
        
        # 関係性スコアの更新
        score_change = (final_score - 0.5) * 0.1
        couple['relationship_score'] += score_change
        couple['relationship_score'] = max(0, min(1, couple['relationship_score']))
        
        return final_score, couple['relationship_score']

# デート最適化システムの使用例
date_system = DateOptimizationSystem()

# サンプルカップルの追加
couple_preferences = {
    'intimacy_preference': 0.7,
    'excitement_preference': 0.6,
    'budget_consciousness': 0.4
}

date_system.add_couple(
    person_a="Alex",
    person_b="Sam", 
    budget=60,
    preferences=couple_preferences
)

# デート推奨の生成
recommendations = date_system.recommend_date(0)
print("デート推奨(上位3つ):")
for i, (activity, score) in enumerate(recommendations, 1):
    print(f"{i}. {activity['name']}: スコア {score:.3f} (費用: ${activity['cost']})")

# デート結果のシミュレーション
chosen_activity = recommendations[0][0]
outcome_score, new_relationship_score = date_system.simulate_date_outcome(0, chosen_activity)

print(f"\nデート結果:")
print(f"活動: {chosen_activity['name']}")
print(f"結果スコア: {outcome_score:.3f}")
print(f"新しい関係性スコア: {new_relationship_score:.3f}")

まとめ:AIと数学で解き明かす恋愛の法則

この「恋の連立方程式」シミュレーションを通じて、以下のことが分かりました:

主要な発見

  1. 数学的モデル化の有効性:恋愛関係も数式で表現し、予測可能な側面があることが確認できました。
  2. AIマッチングの精度:機械学習アルゴリズムを使用することで、従来の直感的なマッチングよりも高い精度を実現できます。
  3. 最適化の重要性:単純な好みの一致だけでなく、複数の要因を総合的に考慮した最適化が重要です。

実用的な応用

  • マッチングアプリの改善:既存のアプリにこれらのアルゴリズムを組み込むことで、より良いマッチングが期待できます。
  • 関係性の予測:カップルの将来的な相性を予測し、問題を事前に察知することが可能です。
  • パーソナライズされた推奨:個人の好みや状況に応じたカスタマイズされたアドバイスが提供できます。

今後の展開

この基本的なシミュレーションをベースに、以下のような発展が考えられます:

  • 深層学習の導入:より複雑なパターンを学習できるニューラルネットワークの活用
  • リアルタイムデータ統合:SNSやスマートフォンからのデータを活用した動的な分析
  • 多人数関係の解析:友人グループや家族関係を含む複雑な人間関係のモデル化

注意点

数学的アプローチは非常に有効ですが、人間の感情や直感も重要な要素であることを忘れてはいけません。このシミュレーションは参考として活用し、最終的な判断は人間が行うことが大切です。

「恋の連立方程式」は、AI技術とプログラミングスキルを活用して、人間関係という複雑な領域に数学的アプローチを適用する面白い例です。技術者として、このような創造的な応用を通じて、AIと人間の感情の架け橋となるシステムを構築していくことが、これからの時代にますます重要になってくるでしょう。

サンプルコード全体の実行環境

この記事で紹介したコードは、以下の環境で動作確認済みです:

  • Python 3.8以上
  • NumPy 1.20以上
  • Matplotlib 3.3以上
  • Pandas 1.3以上
  • Scikit-learn 1.0以上
  • SciPy 1.7以上
  • SymPy 1.8以上

ぜひ実際にコードを実行して、「恋の連立方程式」の世界を体験してみてください!