Problem statement:

Suppose that there are N people in a community, some pairs of which are acquaintances (meaning that they interact with each other) and some pairs of which do not interact with each other at all. Let us say that each person’s happiness is some function of the happiness values of the people that they interact with, as well as possibly some external factors that vary from person to person. In this problem, we would like you to propose one or more models (i.e. give the function) of how happiness values behave, and describe the properties that arise. Some examples of models or functions you may consider are: taking the average of all acquaintances, taking the average of the top 5 happiest acquaintances, or adding time dependency. Your final model should at least somewhat accurately reflect friendships and happiness in the “real world”. In addition, if you had models that at first seemed reasonable but turned out to not behave well, you should also mention these and describe how you ruled them out.

I'll be answering this question with an explanation of my model and an accompanying simulation with a time dependency in Ruby.

Our simulation will take place in a "world" with N people whose happiness and acquaintances will be tracked.

This happiness will be represented as a number $h\in \mathbb{R}$$h \in \mathbb{R}$

We could decide to simply randomly create acquaintances between members of our world, but I think it will be more interesting to instead try to emulate the idea of "groups" of society, like belonging to a family, friend group, etc... These groups can also have only 2 members, in which case they merely represent a relationship.

Each person will be assigned a random number representing their initial mental "inclination" to happiness, no matter their environment. We'll then assign and then randomly create groups of several members, and each group will have an updating "happiness" score based on its members happiness.

Let's build the scaffold of our simulation with Person and Group classes.

In [84]:
class Person
attr_accessor :happiness, :belongs_to, :max_happiness, :min_happiness, :average_variation, :current_step_variation
def initialize
@happiness = (rand -100..100).to_f
@belongs_to = []
@max_happiness = @happiness
@min_happiness = @happiness
@current_step_variation = 0
@average_variation = 0
end

if @happiness > @max_happiness
@max_happiness = @happiness
end
if @happiness < @min_happiness
@min_happiness = @happiness
end
end
end

def avg(array)
array.inject(:+).to_f / array.length # helper method to get an average of the values of an array
end

N_PEOPLE = 1000
$people = (1..N_PEOPLE).map { Person.new } # seed our "People" class Group attr_accessor :members, :happiness def initialize(members) @members = members end end N_GROUPS = 700 MAX_GROUP_SIZE = 10$groups = (0..N_GROUPS-1).map do |group_i| # let's create our random groups
curr_group_size = rand 2..MAX_GROUP_SIZE
members = (0..N_PEOPLE-1).to_a.shuffle.take(curr_group_size) # randomly assemble group members
members.each do |i|
$people[i].belongs_to.append(group_i) end Group.new(members) end avg($people.map {|p| p.happiness}) # average initial happiness in this run of the simulation

(eval):28: warning: already initialized constant N_PEOPLE
(eval):28: warning: previous definition of N_PEOPLE was here
(eval):38: warning: already initialized constant N_GROUPS
(eval):38: warning: previous definition of N_GROUPS was here
(eval):39: warning: already initialized constant MAX_GROUP_SIZE
(eval):39: warning: previous definition of MAX_GROUP_SIZE was here

Out[84]:
-2.807

Now let's keep building our world and already start looking at the parameters of our simulation. We picked the group size as a number from 2-10 so the average should be around 5-6. We can confirm this by computing the average:

In [85]:
group_size_average_group_size = avg($groups.map {|group| group.members.length}) # our assumption confirms itself  Out[85]: 5.97 Now let's start modeling the idea of "group" happiness that affects all its members. Thus, each round of the simulation will compute the 'happiness' of each group, and then factor it in to the happiness of each of its members, either decreasing or increasing it, depending on their difference. This model follows this property: we're more or less likely to be affected by the negative or positive attitude of a given group depending on how many groups we can fall back to. So even if we are member of a negative group (as in its average happiness is less than ours), we can still have a "winning" (as in positive) happiness delta for group happiness if we are member of many other positive groups. This is also true the other way around. In [87]: class Group def carry_happiness @members.each do |i| happiness_delta = (@happiness -$people[i].happiness).to_f * (rand 0.1..0.5)
$people[i].increment_happiness(happiness_delta) end end def update_group_happiness @happiness = @members.inject(0.0) {|sum, i| sum +$people[i].happiness} / @members.length
carry_happiness()
end
end

Out[87]:
:update_group_happiness

Now let's build a "World" class for our simulation: with a time dependency. Each step modifies individual happiness by following the idea of group happiness developed earlier and a random fluctuation that mirrors the ebb and flow of happiness due to all the negative and positives events that affect us.

In this factor we will directly take into account a notion of resilience. If I have a strong and solid network of people on whom I can rely on (i.e. I am member of many groups), the turbulence and wild unpredictability of life will affect me less. Indeed, individuals can fall back on friends and family to either keep them happy in hard times but also sometimes, unfortunately, to weather their happiness when things are going too good.

This is something I'd like to think about more and refine in the future, because I think this model could go a bit further in trying to understand this idea of resilience, especially with regards to when our happiness grows.

Note: this effect is actually already somewhat taken into account when we compute the difference between individual happiness and modify it based on the happiness of the groups he belongs to (notion of group happiness). However, as you can see below I preferred to explicitly factor it into to chaotic variations (like for example a raise at your job which would make your happiness go up, or the death of a family member (down)) of happiness.

In the code, you'll see that this "chaos happiness change" is calculated like this:

In [88]:
class World
def initialize
@epoch = 0
end

def sim_step
$groups.each do |group| group.update_group_happiness end # random mood swings (0..$people.length - 1).each do |i|
change = (rand -65..65).to_f / ($people[i].belongs_to.length + 1)$people[i].increment_happiness(change)
end

# compute average changes for later analysis
(0..$people.length - 1).each do |i|$people[i].average_variation += $people[i].current_step_variation$people[i].current_step_variation = 0
end
end
def start_sim(no_epoch)
@no_epoch = no_epoch
while @epoch != no_epoch
sim_step
@epoch += 1
end
end
end

z = World.new
z.start_sim(500) # starts simulations with 500 steps


Now let's start looking and analyzing our simulation, (and maybe what we can learn about it, on our model and more generally. Let's look at the average happiness population (which is usually not too far from 0, and is not skewed towards a happy (around 100) or sad (-100) population).

In [90]:
$people.inject(0) {|sum, p| sum + p.happiness } /$people.length

Out[90]:
-0.06864538745530707

Now we can look at the distribution of these happiness values depending on the number of groups each person is in. We can notice that due to the highly interlinked nature of this group model, generally, especially for the highly grouped, the average is quite similar. I would like to tweak more with the parameters of the happiness changes, but haven't had time to yet.

In [91]:
n_groups = {}
# seed dictionary object with happiness of all people belonging to 1..n groups
$people.each {|p| n_groups[p.belongs_to.length] = n_groups[p.belongs_to.length] ? n_groups[p.belongs_to.length] + [p.happiness] : [p.happiness]} n_groups.sort.each do |k, v| puts "#{v.length} people belong to #{k} groups." puts "They have an average happiness of #{avg(v)}" end 1  7 people belong to 0 groups. They have an average happiness of 149.57142857142858 65 people belong to 1 groups. They have an average happiness of 6.508037468308729 138 people belong to 2 groups. They have an average happiness of -1.0441589872300674 187 people belong to 3 groups. They have an average happiness of -1.6084946010402499 190 people belong to 4 groups. They have an average happiness of -2.6015487796978123 183 people belong to 5 groups. They have an average happiness of -1.2133300536366372 125 people belong to 6 groups. They have an average happiness of -1.4897871676453698 43 people belong to 7 groups. They have an average happiness of -2.3696848717197208 32 people belong to 8 groups. They have an average happiness of -1.002986522974106 17 people belong to 9 groups. They have an average happiness of -2.5864557343954115 6 people belong to 10 groups. They have an average happiness of 0.34543812392599565 5 people belong to 11 groups. They have an average happiness of -1.5107409919387 1 people belong to 13 groups. They have an average happiness of -3.1790149025319625 1 people belong to 14 groups. They have an average happiness of -4.606471526827767  Out[91]: 1 Now let's look at the average variation between a group's happiness and that of its members. This difference is not immense (in the simulations I've run), but still noticeable, and it reflects the fact that we can act independently and form our happiness, through our own actions and not just be defined by the groups we belong to, even if they have a significant role. In [92]: average_happiness_group_diff = 0$people.each do |p|
if p.belongs_to.length != 0
average_happiness_group_diff += (p.happiness - avg(p.belongs_to.map {|cur_group_index| $groups[cur_group_index].happiness})).abs() end end puts average_happiness_group_diff /$people.length

8.25083850171079


Let's also look at the average variation between the peak and rock bottom happiness of our "people".

In [93]:
puts avg($people.map {|p| p.max_happiness - p.min_happiness})  94.20206655289579  It's interesting to see that our model does not lock our people to some form of permanent equilibrium of happiness, but that there are opportunities for variation and change in happiness. We can also look at the average variation, which is not huge, but still consequent over time, and shows us that this model is not static: In [95]: avg($people.map {|p| p.average_variation})

Out[95]:
2.7383546125446956

Elaborating this model was quite an interesting experience in simulating a very real and social concept like happiness and formalizing it in a way to try and simplify something so complex, as I also had to contemplate properties like the notions of social groups and resilience to events that affect one's happiness.

It also interestingly showed me the extent to which society and groups are so interlinked that changes on one end in the happiness of one person can reverberate and affect many others, thus drawing our "micro-society" of groups to a close average happiness.

I'd like to improve on some aspects of my simulation with which I haven't had time to yet:

• investigate more with parameters and fine tune the way happiness varies over time
• compound simulations into one large "average of averages" simulation where I explore more precisely the different attributes of the model and its changes.
• brainstorm more datapoints and properties that might be interesting to analyze over time, like for example the properties of group happiness and how it shifts.