Creating probability by value from a dict - python-2.7

I'm creating a type of survival game in python, and am creating the different animals you can encounter and "battle" with (users will only encounter one animal at a time). I want certain animals with a lower number associated with them to have a higher probability of showing up then the animals with a higher number associated to them:
POSSIBLE_ANIMALS_ENCOUNTERED = {
"deer": 50, "elk": 75, "mountain lion": 150,
"rat": 5, "raccoon": 15, "squirrel": 5,
"black bear": 120, "grizzly bear": 200, "snake": 5,
}
So, I would like a rat to appear more then a deer and just as much as a squirrel or snake. How can I go about creating probability for the animals inside of the dict based on their value? I'd like it so that the user will see a higher percentage of animals with lower value, and a lower percentage of animals with a higher value.
For example:
The user should see a animal with a value from 1 to 5, 50% of the time (0.5), an animal with a value from 6 to 50 %25 percent of the time (0.25), and any animal with a value higher then that 10 percent of the time (0.10).

You need to codify the percentage likelihood of an encounter based on "health points" (encounter_ranges tuples are from the data in your edit), then do a weighted random choice from your elements. I've put comments inline:
from random import random
from bisect import bisect
POSSIBLE_ANIMALS_ENCOUNTERED = {
"deer": 50, "elk": 75, "mountain lion": 150,
"rat": 5, "raccoon": 15, "squirrel": 5,
"black bear": 120, "grizzly bear": 200, "snake": 5,
}
# codify your ranges. (min, max, encounter %)
encounter_ranges = (
(50, float('inf'), .10),
(6, 50, .25),
(1, 5, .50)
)
# create a list of their probability to be encountered
# replace .items() with .iteritems() if python 2.x
ANIMAL_ENCOUNTER_PERCENTAGE_PER_HP_LIST = []
for k, v in POSSIBLE_ANIMALS_ENCOUNTERED.items():
for encounter_chance in encounter_ranges:
if (v >= encounter_chance[0]) and (v <= encounter_chance[1]):
# multiplied by 100 because we're going to use it in a weighted random below
ANIMAL_ENCOUNTER_PERCENTAGE_PER_HP_LIST.append([k, encounter_chance[2] * 100])
# With our percentages in hand, we need to do a weighted random distribution
# if you're only planning on encountering one animal at a time.
# stolen from http://stackoverflow.com/a/4322940/559633
def weighted_choice(choices):
values, weights = zip(*choices)
total = 0
cum_weights = []
for w in weights:
total += w
cum_weights.append(total)
x = random() * total
i = bisect(cum_weights, x)
return values[i]
# run this function to return the animal you will encounter:
weighted_choice(ANIMAL_ENCOUNTER_PERCENTAGE_PER_HP_LIST)
Note that this approach will always return something -- a 100% chance encounter of some animal. If you want to make the encounters more random, that's a much simpler problem, but I didn't include it as you'd need to specify how you expect this game mechanic to work (random 0-100% chance of any encounter? a percentage chance of the encounter based on the return (50% chance of encounter if rat is returned)? etc).
Note that I wrote this for Python 3, as if you're new to Python, you really should be using 3.x, but I left a comment in what you need to switch if you decide to stick with 2.x.

Related

Where is my logic falling incorrect, if the logic is partially correct? [duplicate]

I understand how the greedy algorithm for the coin change problem (pay a specific amount with the minimal possible number of coins) works - it always selects the coin with the largest denomination not exceeding the remaining sum - and that it always finds the correct solution for specific coin sets.
But for some coin sets, there are sums for which the greedy algorithm fails. For example, for the set {1, 15, 25} and the sum 30, the greedy algorithm first chooses 25, leaving a remainder of 5, and then five 1s for a total of six coins. But the solution with the minimal number of coins is to choose 15 twice.
What conditions must a set of coins fulfil so that the greedy algorithm finds the minimal solution for all sums?
A set which forms a matroid (https://en.wikipedia.org/wiki/Matroid) can be used to solve the coin changing problem by using greedy approach. In brief, a matroid is an ordered pair
M = (S,l) satisfying the following conditions:
S is a finite nonempty set
l is a nonempty family of subsets of S, called the independent subsets,such that if B->l
and A is a subset of B, then A -> l
If A-> l, B-> l and |A| < |B|, then there is some element x-> B-A such that A U {x} ->l
In our question of coin changing, S is a set of all the coins in decreasing order value
We need to achieve a value of V by minimum number of coins in S
In our case, l is an independent set containing all the subsets such that the following holds for each subset: the summation of the values in them is <=V
If our set is a matroid, then our answer is the maximal set A in l, in which no x can be further added
To check, we see if the properties of matroid hold in the set S = {25,15,1} where V = 30
Now, there are two subsets in l:
A = {25} and B= {15,15}
Since |A| < |B|, then there is some element x-> B-A such that A U {x} ->l (According 3)
So, {25,15} should belong to l, but its a contradiction since 25+15>30
So, S is not a matroid and hence greedy approach won't work on it.
In any case where there is no coin whose value, when added to the lowest denomination, is lower than twice that of the denomination immediately less than it, the greedy algorithm works.
i.e. {1,2,3} works because [1,3] and [2,2] add to the same value
however {1, 15, 25} doesn't work because (for the change 30) 15+15>25+1
A coin system is canonical if the number of coins given in change by the greedy algorithm is optimal for all amounts.
This paper offers an O(n^3) algorithm for deciding whether a coin system is canonical, where n is the number of different kinds of coins.
For a non-canonical coin system, there is an amount c for which the greedy algorithm produces a suboptimal number of coins; c is called a counterexample. A coin system is tight if its smallest counterexample is larger than the largest single coin.
This is a recurrence problem. Given a set of coins {Cn, Cn-1, . . ., 1}, such that for 1 <= k <= n, Ck > Ck-1, the Greedy Algorithm will yield the minimum number of coins if Ck > Ck-1 + Ck-2 and for the value V=(Ck + Ck-1) - 1, applying the Greedy Algorithm to the subset of coins {Ck, Ck-1, . . ., 1}, where Ck <= V, results in fewer coins than the number resulting from applying the Greedy Algorithm to the subset of coins {Ck-1, Ck-2, . . ., 1}.
The test is simple: for `1 <= k <= n test the number of coins the Greedy Algorithm yields for a value of Ck + Ck-1 - 1. Do this for coin set {Ck, Ck-1, . . ., 1} and {Ck-1, Ck-2, . . ., 1}. If for any k, the latter yields fewer coins than the former, the Greedy Algorithm will not work for this coin set.
For example, with n=4, consider the coin set {C4, C3, C2, 1} = {50,25,10,1}. Start with k=n=4, then V = Cn + Cn-1 - 1 = 50+25-1 = 74 as test value. For V=74, G{50,25,10,1} = 7 coins. G{25, 10, 1} = 8 coins. So far, so good. Now let k=3. then V=25+10-1=34. G{25, 10, 1} = 10 coins but G{10, 1} = 7 coins. So, we know that that the Greedy Algorithm will not minimize the number of coins for the coin set {50,25,10,1}. On the other hand, if we add a nickle to this coin set, G{25, 10, 5, 1} = 6 and G{10, 5, 1} = 7. Likewise, for V=10+5-1=14, we get G{10, 5, 1} = 5, but G{5,1} = 6. So, we know, Greedy works for {50,25,10,5,1}.
That begs the question: what should be the denomination of coins, satisfying the Greedy Algorithm, which results in the smallest worst case number of coins for any value from 1 to 100? The answer is quite simple: 100 coins, each with a different value 1 to 100. Arguably this is not very useful since it linear search of coins with every transaction. Not to mention the expense of minting so many different denominations and tracking them.
Now, if we want to primarily minimize the number of denominations while secondarily minimizing the resulting number of coins for any value from 1 to 100 produced by Greedy, then coins in denominations of powers of 2: {64, 32, 16, 8, 4, 2, 1} result in a maximum of 6 coins for any value 1:100 (the maximum number of 1's in a seven bit number whose value is less than decimal 100). But this requires 7 denominations of coin. The worst case for the five denominations {50, 25, 10, 5, 1} is 8, which occurs at V=94 and V=99. Coins in powers of 3 {1, 3, 9, 27, 81} also require only only 5 denominations to be serviceable by Greedy but also yield a worst case of 8 coins at values of 62 and 80. Finally, using any the five denomination subset of {64, 32, 16, 8, 4, 2, 1} which cannot exclude '1', and which satisfy Greedy, will also result in a maximum of 8 coins. So there is a linear trade-off. Increasing the number of denominations from 5 to 7 reduces the maximum number of coins that it takes to represent any value between 1 and 100 from 8 to 6, respectively.
On the other hand, if you want to minimize the number of coins exchanged between a buyer and a seller, assuming each has at least one coin of each denomination in their pocket, then this problem is equivalent to the fewest weights it takes to balance any weight from 1 to N pounds. It turns out that the fewest number of coins exchanged in a purchase is achieved if the coin denominations are given in powers of 3: {1, 3, 9, 27, . . .}.
See https://puzzling.stackexchange.com/questions/186/whats-the-fewest-weights-you-need-to-balance-any-weight-from-1-to-40-pounds.
Theory:
If the greedy algorithm always produces an optimal answer for a given set of coins, you say that set is canonical.
Stating the best known algorithmic test [O(n^3)] for determining whether an arbitrary set of n coins is canonical, as succinctly as I can:
[c1,c2,..cn] is canonical iff for all w_ij |G(w_ij)| = |M(w_ij)|, 1 < i <= j <= n
where [c1,c2,...cn] is the list of coin denominations sorted descending with cn = 1
G(x) represents the coin vector result of running the greedy algorithm on input x, (returned as [a1, a2,..., an] where ai is the count of ci)
M(x) represents a coin vector representation of x which uses the fewest coins
|V| represents the size of the coin vector V: the total number of coins in the vector
and w_ij is the evaluated value of the coin vector produced by G(c_(i-1) - 1) after incrementing its j'th coin by 1 and zeroing all its coin counts from j+1 to n.
Implementation (JavaScript):
/**
* Check if coins can be used greedily to optimally solve change-making problem
* coins: [c1, c2, c3...] : sorted descending with cn = 1
* return: [optimal?, minimalCounterExample | null, greedySubOptimal | null] */
function greedyIsOptimal(coins) {
for (let i = 1; i < coins.length; i++) {
greedyVector = makeChangeGreedy(coins, coins[i - 1] - 1)
for (let j = i; j < coins.length; j++) {
let [minimalCoins, w_ij] = getMinimalCoins(coins, j, greedyVector)
let greedyCoins = makeChangeGreedy(coins, w_ij)
if (coinCount(minimalCoins) < coinCount(greedyCoins))
return [false, minimalCoins, greedyCoins]
}
}
return [true, null, null]
}
// coins [c1, c2, c3...] sorted descending with cn = 1 => greedy coinVector for amount
function makeChangeGreedy(coins, amount) {
return coins.map(c => {
let numCoins = Math.floor(amount / c);
amount %= c
return numCoins;
})
}
// generate a potential counter-example in terms of its coinVector and total amount of change
function getMinimalCoins(coins, j, greedyVector) {
minimalCoins = greedyVector.slice();
minimalCoins[j - 1] += 1
for (let k = j; k < coins.length; k++) minimalCoins[k] = 0
return [minimalCoins, evaluateCoinVector(coins, minimalCoins)]
}
// return the total amount of change for coinVector
const evaluateCoinVector = (coins, coinVector) =>
coins.reduce((change, c, i) => change + c * coinVector[i], 0)
// return number of coins in coinVector
const coinCount = (coinVector) =>
coinVector.reduce((count, a) => count + a, 0)
/* Testing */
let someFailed = false;
function test(coins, expect) {
console.log(`testing ${coins}`)
let [optimal, minimal, greedy] = greedyIsOptimal(coins)
if (optimal != expect) (someFailed = true) && console.error(`expected optimal=${expect}
optimal: ${optimal}, amt:${evaluateCoinVector(coins, minimal)}, min: ${minimal}, greedy: ${greedy}`)
}
// canonical examples
test([25, 10, 5, 1], true) // USA
test([240, 60, 24, 12, 6, 3, 1], true) // Pound Sterling - 30
test([240, 60, 30, 12, 6, 3, 1], true) // Pound Sterling - 24
test([16, 8, 4, 2, 1], true) // Powers of 2
test([5, 3, 1], true) // Simple case
// non-canonical examples
test([240, 60, 30, 24, 12, 6, 3, 1], false) // Pound Sterling
test([25, 12, 10, 5, 1], false) // USA + 12c
test([25, 10, 1], false) // USA - nickel
test([4, 3, 1], false) // Simple cases
test([6, 5, 1], false)
console.log(someFailed ? "test(s) failed" : "All tests passed.")
Well we really need to reformulate this question...greedy algorithm essentially doing is that it tries to obtain the target value using the provided coin denominations. Any change you make to the greedy algorithm simply change the way of reaching the target value.
It does not account for the minimum coins used....
To put in a better way a safe move does not existed for this problem.
A higher denomination coin may yield target value quickly but it is not a safe move.
Example {50,47,51,2,9} to obtain 100
Greedy choice would be to take highest denomination coin to reach 100 more quickly..
51+47+2
Well it reached but 50+50 should do..
Lets take {50,47,51,9} to obtain 100
If it makes a greedy choice of highest coin
51 it needs for 49 from the set. It does not know whether it is possible or not. It tries to reach 100 but it cannot
And changing greedy choice simply changes the way of reaching the 100
These types of problems creates set of solutions and forms of branch of decision tree.
Today,I solved question similar to this on Codeforces(link will be provided at then end).
My conclusion was that for coin-change problem to get solved by Greedy alogrithm, it should statisfy following condition:-
1.On sorting coin values in ascending order, all values to the greater than current element should be divisible by the current element.
e.g. coins = {1, 5, 10, 20, 100}, this will give correct answer since {5,10, 20, 100} all are divisible by 1,{10,20, 100} all are divisible by 5,{20,100} all are divisible by 10,{100} all are divisible by 20.
Hope this gives some idea.
996 A - Hit the lottery
https://codeforces.com/blog/entry/60217
An easy to remember case is that any set of coins such that, if they are sorted in ascending order and you have:
coin[0] = 1
coin[i+1] >= 2 * coin[i], for all i = 0 .. N-1 in coin[N]
Then a greedy algorithm using such coins will work.
Depending on the range you're querying, there may be more optimal (in terms of number of coins required) allocation. An example of this is if you're considering the range (6..8) and you have the coins <6, 7, 8> instead of <1, 2, 4, 8>.
The most efficient allocation of coins that is complete over N+ is at equality of the above set of rules, giving you the coins 1, 2, 4, 8 ...; which merely is the binary representation of any number. In some sense, conversation between bases is a greedy algorithm in itself.
A proof on the >= 2N inequality is provided by Max Rabkin in this discussion:
http://olympiad.cs.uct.ac.za/old/camp0_2008/day1_camp0_2008_discussions.pdf

DEAP toolbox: to consider different types and ranges of genes in mutation and crossover operators

I am working on a genetic algorithm implementation and I'm using DEAP toolbox.
I've written a code that initializes chromosomes which their first gene is a float number in range of [0.01, 2048], their second gene is again float in range of [0.0001, 10] and their last three genes are boolean. This is my code:
toolbox.register("attr_flt1", random.uniform, 0.01, 2048)
toolbox.register("attr_flt2", random.uniform, 0.0001, 10)
toolbox.register("attr_bool", random.randint, 0, 1)
enter ctoolbox.register("individual", tools.initCycle, creator.Individual,
(toolbox.attr_flt1, toolbox.attr_flt2, toolbox.attr_bool, toolbox.attr_bool, toolbox.attr_bool),
n=1)
There is a sample of created population:
[1817.2852738610263, 6.184224906600851, 0, 0, 1], [1145.7253307024512, 8.618185266721435, 1, 0, 1], ...
Now, I want to do mutation and crossover on my chromosomes by considering differences in the genes types and ranges.
Currently I have an error because a 0 value is produced for the first gene of a chromosome ,after applying crossover and mutation operators, which is wrong with my evaluation function.
Can anyone help me code selection, mutation and crossover using DEAP toolbox that produce new population in ranges defined at first?
If you use the mutation operator mutPolynomialBounded (documented here), then you can specify the interval for each gene.
With the bounds you indicated, perhaps using something such as
eta = 0.5 #indicates degree of ressemblance of mutated individual
indpb = 0.1 #probability of individual to be mutated
low = [0.01, 0.0001, 0, 0, 0] #lower bound for each gene
up = [2048, 10, 1, 1, 1] #upper bound for each gene
toolbox.register('mutate', mutPolynomialBounded(individual, eta, low, up, indpb))
as a mutation function will solve your error.
This way, first gene is in the interval [0.01, 2048], the second gene is in the interval [0.0001, 10] and the last three genes are in the interval [0, 1].
If you also want the last three genes to be either 0 or 1 (but not a float in between), then you might have to implement your own mutation function. For instance, the following function will select random values for each gene using your requirements
def mutRandom(individual, indpb):
if random.random() < indpb:
individual[0] = toolbox.attr_flt1()
individual[1] = toolbox.attr_flt2()
for i in range(2, 5):
individual[i] = toolbox.attr_bool()
return individual,

Pattern for random subsetting with domains in Chapel

Here is my set up. In hockey, players form into "lines" which are on the ice at the same time. A "forward" line is a trio of Left Wing, Center and Right Wing. A "D" line is a pair of Left D and Right D. In beer leagues, you typically dress 13 skaters = 3 Forward lines, 2 D lines plus one Goalie.
Suppose I have 20 people who want to play. I want to construct the lines from 13 random skaters. I have to preserve their names and jersey numbers. I'm wondering if this is the job of Chapel Domains. For instance, something like
var player_ids: domain(1) = {1..20}
var jerseys [player_ids] = [71, 99, 97, ...]
var names [player_ids] = ['Alice', 'Bonobo', 'Changarakoo'...]
It's a simple idea, but now I want to
1. Pick three random players and assign them to Line 1 F
2. Pick three from the remainders and assign the to Line 2 F
...
n-1: Use the player ids to create an indicator matrix (details aren't important)
n: WIN!
The point of n-1 is that I have to be able to reference the player id and jersey number at the end.
What is the correct pattern for this in Chapel?
Here is my suggestion. To draw players without replacement, I conceptually think of shuffling a deck of cards - where each card has a player's name on it. So this code uses Random.shuffle.
use Random;
var player_ids = {1..20};
// jersey number, name are simply keyed off off player_id
// Generate an array of player IDs
var ids_array = [i in player_ids] i;
// Randomly shuffle player IDs
shuffle(ids_array);
// Now select from the shuffled IDs the players for the game.
var cur = 1;
getLine(cur, "Forward1", 3, ids_array);
getLine(cur, "Forward2", 3, ids_array);
getLine(cur, "Forward3", 3, ids_array);
getLine(cur, "D1", 2, ids_array);
getLine(cur, "D2", 2, ids_array);
getLine(cur, "Goalie", 1, ids_array);
proc getLine(ref curIndex, lineName, playersNeeded, ids_array) {
writeln("Line ", lineName, ":");
for i in 1..playersNeeded {
writeln(" player ", ids_array[curIndex]); // would use name & jersey..
curIndex += 1;
}
}
The Beer-Hockey Team Coach can use this conceptalso( live >>> online ) ( sure, numbers will vary, no fixed RNG-seed was set )
Let's go a bit deeper into the process. The randomised selection is the mathematically harder part of the story ( where compliance will complicate the issues more in domains outside the skating ring, than for the Coach himself (ref. below ) ).
So, let's accept that the Team setup is a static-map, where players' ordinals map onto the F_line{1..3,1..3}, D_line{1..2,1..2}, G, Rest{1..7}
use Random;
var aRandomTEAM = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]; // a known, contiguous ENUM of <anonymised_HASH_ID#s>
permutation( aRandomTEAM ); // a known, static MAP of aRandomTEAM -> F_line{1..3}, D_line1{1..2}, G, Rest
for id in {1..13}{
writeln( " a Static MAP position of TEAM[",
id, "]: will be played by anonymised_HASH_ID#( ",
aRandomTEAM[id], " )"
);
}
For human-readable inspection, this produces:
a Static MAP position of TEAM[1]: will be played by anonymised_HASH_ID#( 20 )
a Static MAP position of TEAM[2]: will be played by anonymised_HASH_ID#( 5 )
a Static MAP position of TEAM[3]: will be played by anonymised_HASH_ID#( 11 )
a Static MAP position of TEAM[4]: will be played by anonymised_HASH_ID#( 4 )
a Static MAP position of TEAM[5]: will be played by anonymised_HASH_ID#( 15 )
a Static MAP position of TEAM[6]: will be played by anonymised_HASH_ID#( 7 )
a Static MAP position of TEAM[7]: will be played by anonymised_HASH_ID#( 16 )
a Static MAP position of TEAM[8]: will be played by anonymised_HASH_ID#( 12 )
a Static MAP position of TEAM[9]: will be played by anonymised_HASH_ID#( 8 )
a Static MAP position of TEAM[10]: will be played by anonymised_HASH_ID#( 18 )
a Static MAP position of TEAM[11]: will be played by anonymised_HASH_ID#( 19 )
a Static MAP position of TEAM[12]: will be played by anonymised_HASH_ID#( 17 )
a Static MAP position of TEAM[13]: will be played by anonymised_HASH_ID#( 3 )
hovever, the machine-readable post-processing may map these onto requested arrays, keep the sensitive personal details safe and separate, having the GUUID#-reference links into names and all other details safe. The referential integrity is both cheap and safe and an implementation of static unique associative mapping from ( intentionally ) contiguous ordinals onto a proxy-anonymising HashTable is trivial ( ref. Opaque Domains and Arrays for possible further inspirations ).
Legal Warning:
A due care ought be taken if using a randomisation in regulated domains, where a compliance has to be documented and positive proofs of methods' robustness performed and validated.
Documentation may bring more details on known risks for using the current randomisation implementations in some legally demanding domains:
Permuted Linear Congruential Random Number Generator
This module provides PCG random number generation routines. See http://www.pcg-random.org/ and the paper, PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation by M.E. O'Neill.
Of a particular attention ought be some known potential restrictions, like:
Note
For integers, this class uses a strategy for generating a value in a particular range that has not been subject to rigorous study and may have statistical problems.
For real numbers, this class generates a random value in [max, min] by computing a random value in [0,1] and scaling and shifting that value. Note that not all possible floating point values in the interval [min, max] can be constructed in this way.
Remarks like this should always attract due attention of Compliance Officers, so as to carefully pre-validate a feasibility of use within their intended ( regulated ) problem-domain mandatory practices and controlled-environments' requirements.

Python3 how to create a list of partial products

I have a very long list (of big numbers), let's say for example:
a=[4,6,7,2,8,2]
I need to get this output:
b=[4,24,168,336,2688,5376]
where each b[i]=a[0]*a[1]...*a[i]
I'm trying to do this recursively in this way:
b=[4] + [ a[i-1]*a[i] for i in range(1,6)]
but the (wrong) result is: [4, 24, 42, 14, 16, 16]
I don't want to compute all the products each time, I need a efficient way (if possible), because the list is very long
At the moment this works for me:
b=[0]*6
b[0]=4
for i in range(1,6): b[i]=a[i]*b[i-1]
but it's too slow. Any ideas? Is it possible to avoid "for" or to speedup it in other way?
You can calculate the product step-by-step since every next calculation heavily depends on the previous one.
What I mean is:
1) Compute the product for the first i - 1 numbers
2) The i-th product will be equal to a[i] * product of the last i - 1 numbers
This method is called dynamic programming
Dynamic programming (also known as dynamic optimization) is a method for solving a complex problem by breaking it down into a collection of simpler subproblems, solving each of those subproblems just once, and storing their solutions
This is the implementation:
a = [4, 6, 7, 2, 8, 2]
b = []
product_so_far = 1
for i in range(len(a)):
product_so_far *= a[i]
b.append(product_so_far)
print(b)
This algorithm works in linear time (O(n)), which is the most efficient complexity you'll get for such a task
If you want a little optimization, you could generate the b list to the predefined length (b = [0] * len(a)) and, instead of appending, you would do this in a loop:
b[i] = product_so_far

Scalacheck, generator for lists between size 5 and 12

I can find many examples of setting maximum sizes for generators, but how do I generate lists between a min and max length?
A neat property about generators is they are composable, so you can simply compose a generator for the length of your list with a listOfN generator.
for {
numElems <- Gen.choose(5, 12)
elems <- Gen.listOfN(numElems, elemGenerator)
} yield elems
I am waking up ghosts here, but incase anyone comes along:
As per my comment on the accepted answer, that solution randomly decides the maximum length between 5 and 12, but the actual size of the generated list could still be 0 (zero) or anything below 5, in that case.
I think the following does what the OP describes:
Gen
.listOfN(12, elemGenerator)
.suchThat(_.size >= 5)
Would be cool if there was an API to conveniently generate this, or say a list of an exact size, N.
Update (2022/12/22)... as I realise I never actually answered the OP's question (and prompted by a recent comment):
def listOf[E](min: Int, max: Int, factor: Int = 4, retries: Int = 10000)(implicit gen: Gen[E]): Gen[List[E]] = for {
size <- Gen.choose(min, max)
list <- Gen.listOfN(factor * size, gen).retryUntil(_.size >= min, retries).flatMap(_.take(size))
} yield list
The factor is some attempt to increase the odds that within 10,000 tries (ScalaCheck's default), a list greater than size min will be generated; perhaps too large a factor will skew away from min-sized lists.
Alternatively, if you like Cats and friends (cats-scalacheck):
def listOf[E](min: Int, max: Int)(implicit gen: Gen[E]): List[E] = for {
size <- Gen.choose(min, max)
list <- List.fill(size)(gen).sequence
} yield list
I cannot attest to what happens in cats-scalacheck; maybe they also use suchThat or retryUntil.