Skip to content

Instantly share code, notes, and snippets.

@tarekeldeeb
Last active July 6, 2024 14:11
Show Gist options
  • Save tarekeldeeb/e8630ce38876fbcd5942a7d371c03665 to your computer and use it in GitHub Desktop.
Save tarekeldeeb/e8630ce38876fbcd5942a7d371c03665 to your computer and use it in GitHub Desktop.
"""
Blog post: https://tarekeldeeb.github.io/quran-table-for-moqantereen
------------------
Problem Definition
------------------
Try to find the optimal Sura words distribution among a given number of days with the following constraints:
- Each day should have 1000+ aya (Days 6 or less)
- The difference of word counts at each day is minimal
This is basically a https://en.wikipedia.org/wiki/Multiway_number_partitioning Problem (NP-hard)
The result is populated in https://docs.google.com/spreadsheets/d/1xGWmfp1Ucinxae14whkBmOaPAktFOnryAXB95CvYEAo
"""
from copy import deepcopy
from pulp import *
number_days = 3 #Valid Numbers 6-3
##
# Quran Data is compiled based on: https://github.com/tarekeldeeb/quranquiznet/blob/master/www/_model_/utils.js
# which is further based on https://tanzil.net/download/ dataset
#
sura_name = ['البقرة','آلعمران','النساء','المائدة','الأنعام','الأعراف','الأنفال','التوبة','يونس',
'هود','يوسف','الرعد','إبراهيم','الحجر','النحل','الإسراء','الكهف','مريم','طه','الأنبياء','الحج',
'المؤمنون','النور','الفرقان','الشعراء','النمل','القصص','العنكبوت','الروم','لقمان','السجدة',
'الأحزاب','سبأ','فاطر','يس','الصافات','ص','الزمر','غافر','فصلت','الشورى','الزخرف','الدخان',
'الجاثية','جزء الأحقاف','جزء الذاريات','جزء قد سمع','جزء تبرك','جزء عم']
sura_words = [
6120,3485,3751,2808,3054,3324,1237,2498,1837,1921,1781,857,834,659,1848,1560,1583,965,1339,
1173,1278,1054,1320,897,1322,1155,1434,980,821,550,376,1291,887,779,729,865,737,1176,1223,
798,864,834,350,492,2482,2706,2654,2705,2455
]
sura_ayahs = [
286,200,176,120,165,206,75,129,109,123,111,43,52,99,128,111,110,98,
135,112,78,118,64,77,227,93,88,69,60,34,30,73,54,45,83,182,88,75,85,
54,53,89,59,37,165,429,137,431,564]
def lists_to_dict(keys, values):
dict_ = {} # using naive method to convert lists to dictionary
for key in keys:
for value in values:
dict_[key] = value
values.remove(value)
break
return dict_
dict_words = lists_to_dict(sura_name, deepcopy(sura_words))
dict_ayahs = lists_to_dict(sura_name, deepcopy(sura_ayahs))
prob = LpProblem("Moqantareen-Problem", LpMaximize)
parts = range(number_days)
items = range(len(sura_name))
min_value = LpVariable("min_value", cat=LpContinuous)
vars = [
[LpVariable(f"x_{item}_{part}", lowBound=0, upBound=1, cat=LpBinary)
for part in parts]
for item in items
] # vars[i][j] is 1 iff item i is in part j.
prob += min_value # Objective function: maximize min_value
for item in items: # Constraints: each item must be in exactly one part.
prob += (lpSum([vars[item][part] for part in parts]) == 1)
for part in parts: # Constraint: the sum of each part must be at least min_value (by definition of min_value).
prob += (min_value <= pulp.lpSum([vars[item][part]*dict_words[sura_name[item]] for item in items]))
prob += (1000 <= pulp.lpSum([vars[item][part]*dict_ayahs[sura_name[item]] for item in items]))
pulp.PULP_CBC_CMD(timeLimit=100).solve(prob)
# The status of the solution is printed to the screen
print("Status:", LpStatus[prob.status])
# Each of the variables is printed with it's resolved optimum value
res = [-1]*len(sura_name)
for v in prob.variables():
if v.varValue == 1.0:
tok = v.name.split('_')
#print(sura_name[int(tok[1])], ' ==> ', (int(tok[2])+1))
res[int(tok[1])] = int(tok[2])+1
print(res)
print("يمكنك نسخ القيم الاخيرة في الجدول")
@drahmedh
Copy link

drahmedh commented Jul 6, 2024

بارك الله فيك وكتب اجر من صلى بهذه التقسيم الرائع وجزاك الله خبر

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment