2424##############################################################################
2525# 1.5) SELECT "JACK" AND "JILL" AS MIDDLE-PERFORMING PROFILES
2626##############################################################################
27- # For Jack, we choose the man whose average probability (from women liking men)
28- # is closest to the overall average among men.
2927man_avgs = prob_women_likes_men .mean (axis = 0 )
3028overall_man_avg = man_avgs .mean ()
3129jack_id = (man_avgs - overall_man_avg ).abs ().idxmin ()
3230
33- # For Jill, we choose the woman whose average probability (from men liking women)
34- # is closest to the overall average among women.
3531woman_avgs = prob_men_likes_women .mean (axis = 0 )
3632overall_woman_avg = woman_avgs .mean ()
3733jill_id = (woman_avgs - overall_woman_avg ).abs ().idxmin ()
@@ -87,11 +83,13 @@ def run_dating_simulation(
8783 random .seed (random_seed )
8884
8985 # Simulation state dictionaries.
90- # For incoming likes, store tuples: (sender, sent_day)
9186 incoming_likes = {uid : [] for uid in all_user_ids }
9287 matches = {uid : set () for uid in all_user_ids }
9388 likes_sent = {uid : set () for uid in all_user_ids }
94- daily_logs = [] # list of DataFrames (one per day)
89+ daily_logs = []
90+
91+ # NEW: track which candidates each user has already seen, so they don't reappear
92+ already_seen = {uid : set () for uid in all_user_ids }
9593
9694 # Loop over simulation days.
9795 for day in range (1 , num_days + 1 ):
@@ -100,51 +98,53 @@ def run_dating_simulation(
10098 random .shuffle (login_order )
10199
102100 for user in login_order :
103- # Determine candidate pool ( opposite gender, not already matched).
101+ # Candidate pool: opposite gender, not matched, not already seen
104102 if user .startswith ("W" ):
105- candidate_pool = [cid for cid in all_men_ids if cid not in matches [user ]]
103+ candidate_pool = [
104+ cid for cid in all_men_ids
105+ if cid not in matches [user ] and cid not in already_seen [user ]
106+ ]
106107 get_prob = lambda cand : prob_women_likes_men .loc [user , cand ]
107108 get_reciprocal = lambda cand : prob_men_likes_women .loc [cand , user ]
108109 else :
109- candidate_pool = [cid for cid in all_women_ids if cid not in matches [user ]]
110+ candidate_pool = [
111+ cid for cid in all_women_ids
112+ if cid not in matches [user ] and cid not in already_seen [user ]
113+ ]
110114 get_prob = lambda cand : prob_men_likes_women .loc [user , cand ]
111115 get_reciprocal = lambda cand : prob_women_likes_men .loc [cand , user ]
112116
113- # Build a lookup from candidate -> sent_day for those who already liked user.
117+ # Build a lookup from candidate -> earliest sent_day
114118 incoming_for_user = {}
115119 for sender , sent_day in incoming_likes [user ]:
116- # If multiple incoming likes from the same candidate, take the earliest.
117120 if sender not in incoming_for_user or sent_day < incoming_for_user [sender ]:
118121 incoming_for_user [sender ] = sent_day
119122
120- # Build a combined candidate list.
123+ # Build the combined candidate list
121124 candidate_info = []
122125 for cand in candidate_pool :
123126 if cand in incoming_for_user :
124- # Candidate already liked user: use score = P_ij.
125127 score = get_prob (cand )
126- candidate_info .append ({
127- "CandidateID" : cand ,
128- "Score" : score ,
129- "Source" : "incoming" ,
130- "SentDay" : incoming_for_user [cand ]
131- })
128+ source = "incoming"
129+ sent_d = incoming_for_user [cand ]
132130 else :
133- # Fresh candidate.
134- q = len (incoming_likes [cand ]) # pending likes for candidate
135- score = get_prob (cand ) * (1 / (1 + weight_queue_penalty * q )) * (get_reciprocal (cand ) ** weight_reciprocal )
136- candidate_info .append ({
137- "CandidateID" : cand ,
138- "Score" : score ,
139- "Source" : "fresh" ,
140- "SentDay" : day # fresh likes are sent today
141- })
131+ q = len (incoming_likes [cand ]) # how many are pending for cand
132+ score = get_prob (cand ) * (1 / (1 + weight_queue_penalty * q )) \
133+ * (get_reciprocal (cand ) ** weight_reciprocal )
134+ source = "fresh"
135+ sent_d = day
136+ candidate_info .append ({
137+ "CandidateID" : cand ,
138+ "Score" : score ,
139+ "Source" : source ,
140+ "SentDay" : sent_d
141+ })
142142
143- # Sort the combined list by score ( descending) and select top daily_queue_size.
143+ # Sort by descending score, pick top daily_queue_size
144144 candidate_info_sorted = sorted (candidate_info , key = lambda x : x ["Score" ], reverse = True )
145145 selected_candidates = candidate_info_sorted [:daily_queue_size ]
146146
147- # Process each selected candidate.
147+ # Process each selected candidate
148148 for cand_record in selected_candidates :
149149 cand = cand_record ["CandidateID" ]
150150 source = cand_record ["Source" ]
@@ -154,25 +154,27 @@ def run_dating_simulation(
154154 decision = "Pass"
155155 match_formed = False
156156
157+ # Remove the incoming like once user sees it
158+ if source == "incoming" :
159+ for idx , (s , sd ) in enumerate (incoming_likes [user ]):
160+ if s == cand :
161+ del incoming_likes [user ][idx ]
162+ break
163+
164+ # Decide like or pass
157165 if roll < like_prob :
158166 decision = "Like"
159- # If candidate has already liked user, a match is formed.
160167 if user in likes_sent [cand ]:
168+ # match is formed
161169 match_formed = True
162170 matches [user ].add (cand )
163171 matches [cand ].add (user )
164172 else :
165173 likes_sent [user ].add (cand )
166- # For fresh candidates, add this like to candidate's incoming likes.
167174 if source == "fresh" :
168175 incoming_likes [cand ].append ((user , day ))
169- # If the candidate came from the incoming list, remove that pending like.
170- if source == "incoming" :
171- for idx , (s , sd ) in enumerate (incoming_likes [user ]):
172- if s == cand :
173- del incoming_likes [user ][idx ]
174- break
175- delay = day - sent_day # 0 if fresh; >0 if pending from an earlier day
176+
177+ delay = day - sent_day
176178 day_records .append ({
177179 "Day" : day ,
178180 "UserID" : user ,
@@ -185,6 +187,10 @@ def run_dating_simulation(
185187 "MatchFormed" : match_formed ,
186188 "Delay" : delay
187189 })
190+
191+ # Mark cand as seen
192+ already_seen [user ].add (cand )
193+
188194 daily_logs .append (pd .DataFrame (day_records ))
189195
190- return daily_logs , matches , incoming_likes
196+ return daily_logs , matches , incoming_likes
0 commit comments