An Exact Algorithm of Polynomial Complexity for the Subset Sum Problem

# ssp.py - An Exact Algorithm of Polynomial Complexity for the Subset Sum Problem
#
# Andrea Bianchini, 2024, Common Creative CC BY-NC 4.0. 
#
# see also https://es-andreabianchini.it/ssp_eng.pdf
#

from random import randrange
import math

try:
    inp = open("input.txt","rt")
    n=int(inp.readline())
    c=int(inp.readline())
    wmin=int(inp.readline())
    wmax=int(inp.readline())
    inp.close()
    w=sorted([int(wmin+randrange(wmax-wmin)) for i in range(n)])
    c1=[0 for i in range(n)]
    sol=[0 for i in range(n)]
    bestsolu=[0 for i in range(n)]    
    niter=0
    bestsol=0
    bestsolk=0

except Exception:
    print(Exception)
    print()
    print("ssp, Andrea Bianchini, 2024, Common Creative CC BY-NC 4.0.")
    print("USAGE:")
    print("ssp")
    print("ssp reads input data from input.txt")
    print("WHERE input.txt is in the form:")
    print("n     # number of items")
    print("c     # capacity of the knapsack")
    print("wmin  # min item's weight")
    print("wmax  # max item's weight")
    print()
    
def solve():
    global n
    global c
    global w
    global bestsolu
    global sol
    global bestsol
        
        
    bestsol=0
    solvalue=0
    divi=1
    T=int(pow(2,n-1))
    while(T>1 and bestsol<c):
        t=0
        divi=1
        while(divi<int(pow(2,n)) and bestsol<c):

            #print(bin(T),bin(t),bestsol)


            sol=list(map(int,bin(t+int(T/divi))[2:].zfill(n)[::-1]))
            gt=sum([sol[i]*w[i] for i in range(n)])
            if (gt<=c):
                t=t+int(T/divi)

            divi*=2
            
            solvalue=gt
            if (bestsol<solvalue and solvalue<=c):
                bestsolu=list(map(int,bin(t)[2:].zfill(n)[::-1]))
                bestsol=solvalue

        T=int(T/2)
                   
    return

try:
    while(True):
        w=sorted([int(wmin+randrange(wmax-wmin)) for i in range(n)])
        
        print("n="+str(n))
        print("c="+str(c))
        print("wmin="+str(wmin))
        print("wmax="+str(wmax))
        print("w = ",w)
        print()

        bestsol=0
        
        solve()
    
        solvalue=sum([bestsolu[i]*w[i] for i in range(n)])

        print()
        print("Solution="+str(solvalue))
        print("Sol. Vector = ",bestsolu)
        print("Sol. Items = ",[w[i] for i in range(n) if bestsolu[i]==1])
        print("Hit return...")
        input()
except Exception:
    print(Exception.message)

input.txt file example:

50
10000
200
4000

Execution example:

n=50
c=10000
wmin=200
wmax=4000
w =  [257, 537, 580, 580, 664, 671, 688, 755, 844, 939, 953, 960, 1014, 1095, 1250, 1359, 1417, 1463, 1513, 1527, 1575, 1629, 1737, 1738, 1817, 1841, 1935, 1942, 2115, 2240, 2318, 2330, 2565, 2606, 2681, 2692, 2788, 3075, 3112, 3196, 3201, 3230, 3257, 3368, 3400, 3440, 3508, 3512, 3782, 3938]


Solution=10000
Sol. Vector =  [1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Sol. Items =  [257, 580, 580, 664, 671, 688, 755, 844, 939, 953, 960, 1014, 1095]
Hit return...

n=50
c=10000
wmin=200
wmax=4000
w =  [293, 419, 439, 576, 635, 672, 978, 1031, 1063, 1120, 1229, 1285, 1288, 1446, 1660, 1800, 1978, 2014, 2033, 2034, 2040, 2148, 2185, 2193, 2201, 2479, 2506, 2521, 2540, 2553, 2585, 2673, 2799, 2976, 3051, 3089, 3287, 3371, 3379, 3390, 3438, 3581, 3613, 3661, 3696, 3753, 3763, 3766, 3834, 3836]


Solution=10000
Sol. Vector =  [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Sol. Items =  [293, 2201, 2479, 2506, 2521]
Hit return...

https://es-andreabianchini.it/ssp_eng.pdf

Andrea Bianchini 2024.

Imparare a programmare in Python

Potete trovare a questo indirizzo->https://developers.google.com/edu/python una guida completa per iniziare a programmare in Python, valida anche per chi già sa programmare come comodo riferimento. Include esempi, esercizi e video.

Di seguito un estratto dell’introduzione:

“Welcome to Google’s Python Class — this is a free class for people with a little bit of programming experience who want to learn Python. The class includes written materials, lecture videos, and lots of code exercises to practice Python coding. These materials are used within Google to introduce Python to people who have just a little programming experience. The first exercises work on basic Python concepts like strings and lists, building up to the later exercises which are full programs dealing with text files, processes, and http connections. The class is geared for people who have a little bit of programming experience in some language, enough to know what a “variable” or “if statement” is. Beyond that, you do not need to be an expert programmer to use this material.

To get started, the Python sections are linked at the left — Python Set Up to get Python installed on your machine, Python Introduction for an introduction to the language, and then Python Strings starts the coding material, leading to the first exercise. The end of each written section includes a link to the code exercise for that section’s material. The lecture videos parallel the written materials, introducing Python, then strings, then first exercises, and so on. At Google, all this material makes up an intensive 2-day class, so the videos are organized as the day-1 and day-2 sections.

This material was created by Nick Parlante working in the engEDU group at Google. Special thanks for the help from my Google colleagues John Cox, Steve Glassman, Piotr Kaminksi, and Antoine Picard. And finally thanks to Google and my director Maggie Johnson for the enlightened generosity to put these materials out on the internet for free under the Creative Commons Attribution 2.5 license — share and enjoy!”

Lettura del flusso MIDI di uno strumento

# mido_test.py
#
# Questo programma dimostra l'utilizzo della libreria MIDO 
# per la gestione dei dispositivi MIDI.
# In particolare, questo programma cattura e stampa il flusso midi
# prodotto da una tastiera elettronica.
# Dopo aver selezionato il dispositivo tra quelli disponibili,
# viene effettuato un ciclo di lettura continua sino all'interruzione
# tramite tasto CTRL-C.
#
# by Andrea Bianchini (2021)
#


import mido

o=mido.get_output_names()

for i in range(len(o)):
    print(str(i)+"-"+o[i])

print()
k=int(input("Seleziona il numero corrispondente al dispositivo di interesse:"))
print()

port = mido.open_input(o[k])

try:
    while True:
        for msg in port.iter_pending():
            if  msg.type!="clock":
                print(msg)
except KeyboardInterrupt:
    print("Interrupted by user")

port.close()

Esempio:

0-Midi Through:Midi Through Port-0 14:0
1-Digital Keyboard:Digital Keyboard MIDI 1 20:0
2-Midi Through:Midi Through Port-0 14:0
3-Digital Keyboard:Digital Keyboard MIDI 1 20:0

Seleziona il numero corrispondente al dispositivo di interesse:1

start time=0
control_change channel=3 control=120 value=0 time=0
control_change channel=7 control=120 value=0 time=0
control_change channel=5 control=120 value=0 time=0
control_change channel=3 control=7 value=122 time=0
note_on channel=3 note=67 velocity=40 time=0
note_on channel=3 note=48 velocity=39 time=0
note_on channel=3 note=69 velocity=48 time=0
note_off channel=3 note=67 velocity=0 time=0
note_on channel=3 note=59 velocity=45 time=0
note_off channel=3 note=59 velocity=0 time=0
note_on channel=3 note=55 velocity=42 time=0
note_off channel=3 note=69 velocity=0 time=0
note_off channel=3 note=48 velocity=0 time=0
note_on channel=3 note=67 velocity=33 time=0
note_off channel=3 note=67 velocity=0 time=0
note_off channel=3 note=55 velocity=0 time=0
note_on channel=3 note=65 velocity=34 time=0
note_on channel=3 note=53 velocity=39 time=0
note_on channel=3 note=63 velocity=35 time=0
note_off channel=3 note=65 velocity=0 time=0
note_on channel=3 note=52 velocity=50 time=0
note_off channel=3 note=53 velocity=0 time=0
note_off channel=3 note=63 velocity=0 time=0
note_on channel=3 note=62 velocity=42 time=0
note_off channel=3 note=52 velocity=0 time=0
note_off channel=3 note=62 velocity=0 time=0
note_on channel=3 note=53 velocity=41 time=0
note_on channel=3 note=64 velocity=49 time=0

profMI

# profMI.py
#
# 
# Il professor MII insegna in una classe universitaria formata da
# ragazzi e ragazze. Viene fornita una lista contenente una lista 
# per ogni studente. Il primo numero della lista è l'idendificativo
# dello studente, il secondo numero della lista rappresenta il sesso;
# (0=MASCHIO,1=FEMMINA,2=LGBT). I restanti numeri indicano i voti
# conseguiti per quello studente.
# Il programma determina la media dei voti per genere sessuale.
#
# by Andrea Bianchini (2021)

from random import randint

N = 50
MIN = 18
MAX = 31
NVOTI = 5

#generazione casuale lista dei voti:
cl=[]
for i in range(N):
    student=[]
    student.append(i)
    student.append(randint(0,2))
    for j in range(NVOTI):
        student.append(randint(MIN,MAX))
    cl.append(student)
                   
#calcolo
mediatot=[0.0,0.0,0.0]
ng=[0,0,0]
for i in range(N):
    medias=[0.0,0.0,0.0]
    student=cl[i]
    sex = student[1]
    ng[sex]+=1
    for j in range(NVOTI):
        medias[sex]+=float(student[2+j])
    medias[sex]/=float(NVOTI)
    mediatot[sex]+=medias[sex]

mediatot[0]/=float(ng[0])
mediatot[1]/=float(ng[1])
mediatot[2]/=float(ng[2])

print("Lista studenti = ", cl)
print()
print("Media voti maschi = %.2f" %mediatot[0])
print("Media voti femmine = %.2f" %mediatot[1])
print("Media voti LGBT = %.2f" %mediatot[2])

Esempio:

Lista studenti =  [[0, 0, 23, 23, 22, 24, 28], [1, 0, 22, 18, 18, 24, 28], [2, 0, 30, 25, 28, 21, 25], [3, 1, 23, 18, 23, 24, 30], [4, 0, 23, 19, 23, 25, 30], [5, 0, 19, 24, 30, 29, 26], [6, 1, 19, 21, 21, 20, 22], [7, 1, 27, 30, 18, 26, 20], [8, 1, 30, 24, 29, 21, 23], [9, 2, 31, 19, 27, 30, 25], [10, 2, 31, 27, 21, 31, 22], [11, 1, 19, 25, 19, 31, 20], [12, 2, 29, 24, 24, 26, 29], [13, 0, 26, 23, 25, 26, 29], [14, 0, 25, 29, 23, 21, 29], [15, 0, 19, 28, 22, 20, 29], [16, 1, 28, 21, 24, 22, 18], [17, 2, 19, 22, 27, 25, 22], [18, 1, 31, 22, 18, 27, 20], [19, 2, 21, 25, 25, 19, 23], [20, 2, 27, 23, 30, 25, 21], [21, 1, 26, 29, 23, 30, 30], [22, 1, 29, 22, 18, 30, 26], [23, 0, 23, 30, 28, 20, 19], [24, 1, 27, 29, 28, 19, 29], [25, 0, 25, 27, 22, 24, 31], [26, 1, 30, 26, 28, 26, 28], [27, 0, 18, 22, 30, 27, 26], [28, 1, 27, 18, 25, 24, 18], [29, 0, 31, 23, 24, 21, 30], [30, 2, 30, 28, 19, 24, 25], [31, 2, 23, 22, 23, 22, 19], [32, 2, 29, 31, 30, 25, 29], [33, 1, 30, 21, 29, 20, 23], [34, 1, 21, 26, 27, 23, 18], [35, 1, 29, 25, 20, 29, 25], [36, 2, 18, 21, 20, 22, 31], [37, 1, 30, 20, 19, 26, 20], [38, 0, 26, 19, 22, 22, 19], [39, 0, 31, 27, 19, 29, 18], [40, 2, 26, 26, 23, 24, 21], [41, 2, 28, 26, 23, 22, 25], [42, 2, 29, 27, 29, 25, 24], [43, 1, 29, 21, 18, 31, 29], [44, 1, 24, 22, 20, 31, 21], [45, 1, 20, 21, 30, 28, 23], [46, 0, 26, 24, 29, 26, 23], [47, 1, 22, 26, 26, 31, 26], [48, 2, 18, 23, 25, 23, 21], [49, 0, 18, 22, 26, 23, 23]]

Media voti maschi = 24.43
Media voti femmine = 24.39
Media voti LGBT = 24.70
>>> 

Coppie di numeri a differenza costante

# kdiff.py
#
# 
# Questo programma risolve il seguente problema :
# Dato un insieme s di N numeri interi trovare
# tutte le coppie di interi la cui differenza è pari a K
#
# by Andrea Bianchini (2021)

from random import randint

N = 50
MIN = 1
MAX = 1000
K = randint(MIN,MAX/2)

s = [randint(MIN,MAX) for _ in range(N)]


def f(x,y):
    global K
    if abs(x-y)==K:
        return True
    else:
        return False
    
res =[]

for x in range(N-1):
    for y in range(x+1,N):
        if f(s[x],s[y]):
            res.append((s[x],s[y]))

print("s =",s)
print()
print("K = %d" %K)
print("coppie =",res)

Esempio 1:

s = [240, 662, 961, 108, 802, 232, 954, 99, 542, 775, 668, 988, 637, 96, 135, 549, 111, 678, 86, 596, 912, 13, 191, 516, 118, 88, 837, 632, 870, 843, 542, 569, 819, 579, 411, 154, 743, 89, 95, 686, 604, 503, 511, 859, 305, 671, 161, 510, 164, 906]

K = 10
coppie = [(108, 118), (99, 89), (668, 678), (96, 86), (569, 579), (154, 164)]
>>> 

Esempio 2:

s = [327, 847, 660, 722, 431, 287, 79, 542, 354, 514, 703, 522, 764, 75, 904, 789, 103, 801, 929, 884, 964, 818, 992, 458, 369, 900, 239, 671, 120, 904, 421, 342, 403, 314, 495, 794, 119, 140, 810, 237, 333, 639, 676, 705, 134, 802, 528, 20, 452, 2]

K = 105
coppie = [(239, 134), (342, 237), (810, 705)]
>>> 

Algoritmi genetici

# DEAP_generico.py
#
# esempio di utilizzo della libreria per la computazione genetica evolutiva
# dei problemi denominata DEAP.
# in questo caso ho utilizzato lo scheletro della risoluzione di un
# problema generico per risolvere il problema di ottimizzazione
# noto come Knapsack Problem.
# documentazione su : https://deap.readthedocs.io/en/master/index.html
#
# by Andrea Bianchini (2021)



import random

from deap import base
from deap import creator
from deap import tools

IND_SIZE = 50
C = 10000
MIN_WEIGHT = int(C*0.01)
MAX_WEIGHT = int(C*0.07)
MAX=0
sol=[]
items=[random.randint(MIN_WEIGHT,MAX_WEIGHT) for _ in range(IND_SIZE)]

creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)

toolbox = base.Toolbox()
# Attribute generator 
toolbox.register("attr_bool", random.randint, 0, 1)
# Structure initializers
toolbox.register("individual", tools.initRepeat, creator.Individual, 
    toolbox.attr_bool, IND_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

def evalOneMax(individual):
    global sol
    global MAX
    e1 = sum([items[i]*individual[i] for i in range(len(individual))])
    if e1>MAX and e1<=C:
        MAX = e1
        sol=individual
    return e1,

toolbox.register("evaluate", evalOneMax)
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=3)

summa=0
def main():
    global summa
    pop = toolbox.population(n=300)

    # Evaluate the entire population
    fitnesses = list(map(toolbox.evaluate, pop))
    for ind, fit in zip(pop, fitnesses):
        ind.fitness.values = fit

    # CXPB  is the probability with which two individuals
    #       are crossed
    #
    # MUTPB is the probability for mutating an individual
    CXPB, MUTPB = 0.5, 0.2

    # Extracting all the fitnesses of 
    fits = [ind.fitness.values[0] for ind in pop]

    # Variable keeping track of the number of generations
    g = 0
    
    # Begin the evolution
    
    while max(fits) < C and g < 1000:
        # A new generation
        g = g + 1
        print("-- Generation %i --" % g)

        # Select the next generation individuals
        offspring = toolbox.select(pop, len(pop))
        # Clone the selected individuals
        offspring = list(map(toolbox.clone, offspring))

        # Apply crossover and mutation on the offspring
        for child1, child2 in zip(offspring[::2], offspring[1::2]):
            if random.random() < CXPB:
                toolbox.mate(child1, child2)
                del child1.fitness.values
                del child2.fitness.values

        for mutant in offspring:
            if random.random() < MUTPB:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        # Evaluate the individuals with an invalid fitness
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        pop[:] = offspring

        # Gather all the fitnesses in one list and print the stats
        fits = [ind.fitness.values[0] for ind in pop]
        
        length = len(pop)
        mean = sum(fits) / length
        sum2 = sum(x*x for x in fits)
        std = abs(sum2 / length - mean**2)**0.5
        
        print("  Min %s" % min(fits))
        print("  Max %s" % max(fits))
        print("  Avg %s" % mean)
        print("  Std %s" % std)

        if max(fits) <=C and summa<max(fits):
            summa = max(fits)

main()
print()
print("Soluzione ottima = %d" %MAX)
print("Lista items completa :")
print(items)
print("Lista items soluzione :")
osol = [items[i]*sol[i] for i in range(IND_SIZE)]
print(osol)

Esempio:

Soluzione ottima = 10000
Lista items completa :
[481, 167, 361, 111, 670, 602, 119, 331, 654, 155, 400, 227, 651, 531, 413, 134, 409, 140, 488, 457, 495, 628, 153, 632, 327, 159, 593, 633, 682, 392, 368, 526, 599, 478, 408, 315, 466, 582, 302, 172, 427, 173, 551, 673, 272, 158, 502, 269, 685, 588]
Lista items soluzione :
[0, 0, 361, 0, 670, 0, 0, 331, 654, 0, 0, 0, 651, 0, 0, 0, 409, 140, 488, 0, 0, 628, 0, 632, 327, 0, 593, 0, 682, 0, 368, 0, 0, 478, 0, 315, 0, 582, 0, 172, 427, 0, 551, 0, 272, 0, 0, 269, 0, 0]
>>> 

Valore atteso, varianza e deviazione standard

# varianza.py
#
# questo programma calcola il valore medio, la varianza
# e la deviazione standard di una lista di numeri
# interi generati casualmente.
#
# by Andrea Bianchini (2021)
#


from random import randint
import math

N = 20
MIN = 0
MAX = 1000

s = [randint(MIN,MAX) for _ in range(N)]

print("La lista generata è la seguente :")
print(s)

# Calcolo valore medio
vm=float(sum(s))
vm=vm/N

# Calcolo varianza
scarti = [(sx-vm)*(sx-vm) for sx in s]
vms = float(sum(scarti))
vms = vms/N

print()
print("Scarti quadratici lista originale : ")
print(scarti)
print()
print("Valore medio lista originale: %.2f" %vm)
print()
print("Varianza lista originale: %.2f" %vms)
print()
print("Deviazione standard lista originale: %.2f" %(math.sqrt(vms)))
print()
print("Deviazione minima lista originale: %.2f " %(math.sqrt(min(scarti))))
print()
print("Deviazione massima lista originale: %.2f " %(math.sqrt(max(scarti))))

Esempio:

La lista generata è la seguente :
[73, 801, 284, 79, 291, 425, 429, 265, 494, 330, 376, 521, 333, 162, 763, 202, 66, 462, 202, 366]

Scarti quadratici lista originale : 
[74638.23999999999, 206843.04, 3868.839999999999, 71395.84, 3047.0399999999986, 6209.440000000001, 6855.840000000002, 6593.439999999998, 21844.840000000004, 262.43999999999966, 888.0400000000006, 30555.040000000005, 174.2399999999997, 33929.64, 173722.24000000002, 20793.639999999996, 78512.04, 13409.640000000003, 20793.639999999996, 392.0400000000005]

Valore medio lista originale: 346.20

Varianza lista originale: 38736.46

Deviazione standard lista originale: 196.82

Deviazione minima lista originale: 13.20 

Deviazione massima lista originale: 454.80 
>>> 

Programmazione Lineare

# pulp1.py
#
# Risoluzione di un problema di programmazione lineare
# tramite la libreria PuLP.
#
# definizione problema by https://www3.diism.unisi.it/~agnetis/esesvolti.pdf
# soluzione in Python tramite utilizzo libreria PuLP, by Andrea Bianchini 2021
#
# Problema:
# Un lanificio produce filato di tipo standard e di tipo speciale
# utilizzando 3 diverse macchine, le cui produzioni orarie sono le seguenti:
# macchina A: 3 matasse standard e 1 speciale
# macchina B: 2 matasse standard e 2 speciali
# macchina C: 2 matasse standard e 1 speciale
# Il mercato richiede almeno 60 matasse standard e 40 di tipo speciale al giorno. I costi
# orari delle due macchine sono: 90 euro per la A, 80 euro per B, 60 euro per C.
# Scrivere un modello di programmazione lineare per determinare la produzione giornaliera
# di costo minimo. (Non occorre imporre il vincolo che le ore giornaliere non superino 24)
#


from pulp import *

a = pulp.LpVariable("a", lowBound=0)
b = pulp.LpVariable("b", lowBound=0)
c = pulp.LpVariable("c", lowBound=0)

problem = pulp.LpProblem("Un semplice problema di min", LpMinimize)

problem += 90*a + 80*b + 60*c, "The objective function"
problem += 3*a + 2*b + 2*c >= 60, "1st constraint"
problem += a + 2*b + c >= 40, "2nd constraint"
problem += a >= 0, "3rd constraint"
problem.solve()

print("Risultati della ottimizzazione:")
for variable in problem.variables():
    print(variable.name + "=" + str(variable.varValue))
print("Costo minimo netto totale: %.1f" %value(problem.objective))

Esempio:

Risultati della ottimizzazione:
a=0.0
b=10.0
c=20.0
Costo minimo netto totale: 2000.0
>>> 

MinMax

# minmax.py
#
# trova il minimo ed il massimo elemento di una lista di interi
# senza utilizzare le funzioni min e max.
#
# by Andrea Bianchini, 2021
#

nums = [30,11,54,78,12,5,63,20,43,26,52]

print("Lista originale:")
print(nums)

print()
print("Lista ordinata :")
nums.sort()
print(nums)

print()
print("Il numero più piccolo è %d" %nums[0])
print("Il numero più grande è %d" %nums[len(nums)-1])

Esempio:

Lista originale:
[30, 11, 54, 78, 12, 5, 63, 20, 43, 26, 52]

Lista ordinata :
[5, 11, 12, 20, 26, 30, 43, 52, 54, 63, 78]

Il numero più piccolo è 5
Il numero più grande è 78
>>> 

Generatore di Password

# GenPassword.py - generatore di password
# Legge dall'input la lunghezza della password desiderata e la probabilità
# della presenza di caratteri speciali espressa come intero tra 0 e 10.
# Fornisce in output la password
#
# by Andrea Bianchini 2021
#

from random import randint

s = "0123456789abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"
sp = "_#:.,;^&%@"

l = int(input("Lunghezza password in caratteri : "))
ps = int(input("Probabilità caratteri speciali (0-10) : "))

pw=""
for i in range(l):
    ln = l * ps/10
    lx = randint(0,l)
    if ps!=0 and lx<=ln:
        pw = pw + sp[randint(0,len(sp)-1)]
    else:
        pw = pw + s[randint(0,len(s)-1)]

print("La password generata è : "+pw)

Esempio:

Lunghezza password in caratteri : 10
Probabilità caratteri speciali (0-10) : 2
La password generata è : A%@Ik.jtm7
>>>