I'm trying to build a simple market model, such as what follows.
breed [ firms firm ]
breed [ consumers consumer ]
firms-own [ price ]
hogus-own [ money stock ]
globals [ m0 ]
There are also 100 customers, and 10 firms. The price of the firms is set randomly from 0 to 10, and each consumer has an initial capital of 100.
to setup
set m0 [ 0 ]
set m0 lput p0 m0
end
to go
tick
buy
sell
set m0 fput p0 m0
end
to-report p0
report [ price ] of one-of firms
end
to-report recent
report item ticks m0
end
to-report past
report item ( ticks - 1 ) m0
end
I've made a list so I can compare the current and past prices of the firm, however I've run into a problem. I want each agent to choose a random 5 firms, and then compare the past and current price. Then, if the price has increase the consumer should buy it (i.e. increase stock). I tried to do it this way
to buy
if (recent > past and capital > 0) [
set capital capital - recent0
set stock stock + 1 ]
end
but I'm running into an issue. Each agent should choose their own, random firm. However because every agent is bound by the same code, they always buy from the same firm. How could I resolve this?
Related
I want to generate a random number within a range while considering a mean value.
I have a solution for generating the range:
turtles-own [age]
to setup
crt 2 [
get-age
]
end
to get-age
let min-age 65
let max-age 105
set age ( min-age + random ( max-age - min-age ) )
end
However, if I use this approach every number can be created with the same probability, which doesn't make much sense in this case as way more people are 65 than 105 years old.
Therefore, I want to include a mean value. I found random-normal but as I don't have a standard deviation and my values are not normally distributed, I can't use this approach.
Edit:
An example: I have two agent typologies. Agent typology 1 has the mean age 79 and the age range 67-90. Agent typology 2 has the mean age 77 and the age range 67-92.
If I implement the agent typologies in NetLogo as described above, I get for agent typlogy 1 the mean age 78 and for agent typology 2 the mean age 79. The reason for that is that for every age the exact same number of agents is generated. This gives me in the end the wrong result for my artificial population.
[Editor's note: Comment from asker added here.]
I want a distribution of values with most values for the min value and fewest values for the max value. However, the curve of the distribution is not necessarily negative linear. Therefore, I need the mean value. I need this approach because there is the possibility that one agent typology has the range for age 65 - 90 and the mean age 70 and another agent typology has the same age range but the mean age is 75. So the real age distribution for the agents would look different.
This is a maths problem rather than a NetLogo problem. You haven't worked out what you want your distribution to look like (lots of different curves can have the same min, max and mean). If you don't know what your curve looks like, it's pretty hard to code it in NetLogo.
However, let's take the simplest curve. This is two uniform distributions, one from the min to the mean and the other from the mean to the max. While it's not decreasing along the length, it will give you the min, max and mean that you want and the higher values will have lower probability as long as the mean is less than the midway point from min to max (as it is if your target is decreasing). The only question is what is the probability to select from each of the two uniform distributions.
If L is your min (low value), H is your max (high value) and M for mean, then you need to find the probability P to select from the lower range, with (1-P) for the upper range. But you know that the total probability of the lower range must equal the total probability of the upper range must equal 0.5 because you want to switch ranges at the mean and the mean must also be the mean of the combined distribution. Therefore, each rectangle is the same size. That is P(M-L) = (1-P)(H-M). Solving for P gets you:
P = (H-M) / (H - L)
Put it into a function:
to-report random-tworange [#min #max #mean]
let prob (#max - #mean) / (#max - #min)
ifelse random-float 1 < prob
[ report #min + random-float (#mean - #min) ]
[ report #mean + random-float (#max - #mean) ]
end
To test this, try different values in the following code:
to testme
let testvals []
let low 77
let high 85
let target 80
repeat 10000 [set testvals lput (random-tworange low high target) testvals]
print mean testvals
end
One other thing you should think about - how much does age matter? This is a design question. You only need to include things that change an agent's behaviour. If agents with age 70 make the same decisions as those with age 80, then all you really need is that the age is in this range and not the specific value.
I used the code below to create 50 turtles and randomly assign them to one of four different strategies (i.e. a, b, c and d):
The problem is that when I decrease the number of created turtles or when I increase the number of strategies, I face a situation where at least one of the strategies is not taken by any turtle.
turtles-own [ my_strategy ]
to setup
;; create 50 turtles and assign them randomly
;; to one of four different strategies
create-turtles 50 [
set my_strategy one-of [ "a" "b" "c" "d" ]
]
end
I need your help here to:
1. Make sure that I do not face a situation where one or more strategies are not taken by any turtle.
2. Make sure that the number of turtles assigned to each strategy is roughly equal.
I tried to solve the problem by using the code below, but it did not work:
turtles-own [ my_strategy ]
to setup
let strategies [ "a" "b" "c" "d" ]
let turtles-num 51
let i 0
create-turtles turtles-num
while [ not any? turtles with [ my_strategy = 0 ] ] [
ifelse i < length strategies - 1 [ set i i + 1 ] [ set i 0 ]
ask n-of ceiling ( turtles-num / length strategies ) turtles with [ my_strategy = 0 ] [
set my_strategy item i strategies
]
]
Thank you for your help.
In general, you should never use who numbers for anything in NetLogo. However, this is one of the very few occasions where it's appropriate.
From comments, you actually want equal (or as close to equal as possible) numbers in each group so you don't need to calculate the number in each group. When turtles are created, they are created with sequential who numbers. So you can use the mod operator to assign them to each strategy in turn.
turtles-own [ my_strategy ]
to setup
;; create 50 turtles and assign them equally
;; to one of four different strategies
create-turtles 50 [
set my_strategy item (who mod 4) [ "a" "b" "c" "d" ]
]
end
I have about 5000 agents (people) in my model. I want to give them an arbitrary number of friends and have reciprocal but random pairing. So if person A chooses person B then person B also chooses person A. My code works fine, but is fairly slow. I will likely want to increase both the number of friends and the number of people in the future. Any quicker suggestions?
ask people
[ let new-links friends - count my-links
if new-links > 0
[ let candidates other people with [ count my-links < friends ]
create-links-with n-of min (list new-links count candidates) candidates
[ hide-link ]
]
]
Note that friends is a global variable in the above code, but my eventual code will probably generalise to have wanted-number-of-friends as an attribute of people.
EDITED Added if new-links > 0 condition so that the nested ask is avoided when no candidates need to be found. This improved speed but still not really scaleable.
Great question. This is actually quite challenging to optimize. The problematic line is:
let candidates other people with [ count my-links < friends ]
This is slow because it has every agent checking with every other agent. With 5000 agents, that's 25,000,000 checks! Unfortunately, there isn't really a good way to optimize this particular line without some fancy data structures.
Fortunately, there is a solution that generalizes really well to generating any degree distribution in the network (which it sounds like that's what you ultimately want). Unfortunately, the solution doesn't translate super well to NetLogo. Here it is though:
let pairs [] ;; pairs will hold a pairs of turtles to be linked
while [ pairs = [] ] [ ;; we might mess up creating these pairs (by making self loops), so we might need to try a couple of times
let half-pairs reduce sentence [ n-values friends [ self ] ] of turtles ;; create a big list where each turtle appears once for each friend it wants to have
set pairs (map list half-pairs shuffle half-pairs) ;; pair off the items of half-pairs with a randomized version of half-pairs, so we end up with a list like: [[ turtle 0 turtle 5 ] [ turtle 0 turtle 376 ] ... [ turtle 1 turtle 18 ]]
;; make sure that no turtle is paired with itself
if not empty? filter [ first ? = last ? ] pairs [
set pairs []
]
]
;; now that we have pairs that we know work, create the links
foreach pairs [
ask first ? [
create-link-with last ?
]
]
It doesn't matter if friends here is a global or a turtle variable. The amount of time this takes depends on the number of times that it needs to try making pairs, which is random. Experimenting, I found that it was usually about 3 seconds with 5000 agents, each with degree 5. This is compared to about 60 seconds on my machine with your original way of doing this (which, for what it's worth, is the way I would recommend when using fewer agents).
After debugging (see NetLogo Efficiently create network with arbitrary degree distribution), the following version is relatively efficient. It constructs an agentset (called lonely below) for the turtles that still need links and deletes them as they get enough links. Removing individual turtles is more efficient than the nested process to create the candidate set each time.
The variable nFriends is a global (with a slider in the original model) that is the target number of links, identical for all agents.
let lonely turtles with [count my-links < nFriends]
ask turtles
[ set lonely other lonely
let new-links nFriends - count my-links
if new-links > 0
[ let chosen n-of min (list new-links count lonely) lonely
create-links-with chosen [ hide-link ]
ask chosen [ if count my-links = nFriends [ set lonely other lonely ] ]
]
]
I want the observer to calculate the mean of the number of floodings of a house (=patch) over the last 10 years (=ticks) when a certain action takes place (in this case, an insurance application by a turtle). This occurrence doesn't occur regularly, it can be regarded as a random occurrence (more or less).
So basically, I need some code that calculates the mean of floodings over the last 10 ticks when insurance application occurs.
Assuming floodings is a patch-variable and you'd like to determine the mean number of floodings for a given patch:
patches-own [
floodings
floodingsHistory
floodingsMean10
]
; At the end of each tick, patches store the current
; number of floodings in a list:
ask patches [ set floodingsHistory fput floodings floodingsHistory ]
; In case of [insurance application] patches (or a certain patch) calculate
; the mean over a sublist that only comprises the values of the latest 10 ticks:
ask patches [ set floodingsMean10 mean (sublist floodingsHistory 0 10) ]
I am a complete algorithm dunce and I have this problem where I need to find the lowest cost of buying an unknown quantity of widgets either by unit price or by buying in batches of x widgets... an example will help I'm sure:-
1) Widget price per unit is $0.05
2) Batches of widgets are priced at $4.00 per 100 widgets
Say I wish to buy 140 widgets -
a) costing it by unit is 140 x $0.05c => $7.00
b) costing it by batch is 2 batches of 100 # $4.00 => $8.00 (the excess 60 widgets can be ignored)
so buying by unit in this case is cheaper by $1.00
However if I wish to buy 190 widgets then -
a) costing it by unit is 190 x $0.05c => $9.50
b) costing it by batch is 2 batches of 100 # $4.00 => $8.00 (the excess 10 widgets can be ignored)
The price is cheaper in this case by batch buying...
So what I need to find out how to programatically find out where is the 'tipping point' between the 2 methods to get the cheapest price.
I hope I have explained it OK and I'm sure it's a simple answer but my brain has faded fast today!
TIA
EDIT::
Ok sorry -- I realise I haven't been as clear as I should -- as someone has pointed out a mix of batches and units is also possible so for the 140 widget example it could also be 1 batch and 40 units.
What I am trying to achieve is to programatically find the cheapest way to purchase X number of widgets priced each at $XX and also given a batch price $YY of NN widgets.
Any excess widgets from buying a batch is no problem i.e. it can be more than X purchased but it cannot be less than X
So for the 140 example 1 batch # $4.00 + 40 units # $0.05 => $6.00 which is the cheapest I think. And
for the 190 example 2 batches is still the cheapest I think as 1 batch + 90 units is $8.50...
I was hoping there would be some neat equation out there which would do this :)
I wrote a basic brute-force style script in python to compare the prices of the two options (up to 1,000 elements). It's not the fastest nor most elegant approach but it appears to work.
The format on the output is: (<unit-count>, (<per-unit-cost>, <per-batch-cost>))
import math
import itertools
import pprint
unitList = range(1000)
pricePerUnit = .05
pricePerBatch = 4.0
numberPerBatch = 100.0
def calculatePerUnit(units):
""" Calculate the price of buying per unit
"""
return units * pricePerUnit
def calculatePerBatch(units):
""" Calculate the price of buying per batch
"""
return math.ceil(units / numberPerBatch) * pricePerBatch
def main():
""" Execute the script
"""
perUnit = map(calculatePerUnit, unitList)
perBatch = map(calculatePerBatch, unitList)
comparisonList = zip(perUnit, perBatch)
perUnitCheaperPriceList = list(itertools.ifilter(lambda x: x[0] < x[1], comparisonList))
perUnitCheaperUnitList = map(lambda x: int(x[0] / .05), perUnitCheaperPriceList)
pprint.pprint(zip(perUnitCheaperUnitList, perUnitCheaperPriceList))
if __name__=="__main__":
main()
And the results:
[gizmo#test ~]$ python TippingPoint.py
[(1, (0.050000000000000003, 4.0)),
... These are sequential values I left out for brevity ...
(79, (3.9500000000000002, 4.0)),
(101, (5.0500000000000007, 8.0)),
... These are sequential values I left out for brevity ...
(159, (7.9500000000000002, 8.0)),
(201, (10.050000000000001, 12.0)),
... These are sequential values I left out for brevity ...
(239, (11.950000000000001, 12.0)),
(301, (15.050000000000001, 16.0)),
... These are sequential values I left out for brevity ...
(319, (15.950000000000001, 16.0))]
The answer to this is astonishingly easy, if you just think of it the other way around. You already know the 'tipping point' in price. It is $8.00. What you do not know is that point in units, which is $8.00 divided by $0.05 (per units) => 160 units.
Please note that there also is the chance of buying 100 units # $4.00 and 60 units # $0.05, resulting in $7.00 total. That is a third possibility that you may or may not account for.
Edit: After your edit, things are getting even more simple:
you have 1 item at $XX, and a batch of NN items at $YY. Assuming that $YY/NN < $XX (meaning, the batch actually saves money), all you have to do is this:
Divide your target quantity by NN, round that down. You will have to buy this number of batches
Multiply the remaining quantity with $XX. If that is more than $YY just buy an extra batch, otherwise buy the remaining quantity at $XX