Kelebek algoritması için fikir istiyorum

Sınıf grupları (9,10,11,12) arasında çok büyük sayı farkları yok. 11’ler 100 kişi fazla mesela. Oturma düzeninde 9, 10,11 ve 12. sınıfların yan yana oturmalarını engelleyerek kopya çekmelerini engellemek amacımız. Veritabanımız şöyle:

sinif tablosu
SinifID
SinifAdi

ogrenci tablosu
OgrID
SinifID
OgrenciAdi

PyQT ile tasarlanmış formda 4 adet listbox oluşturdum. Sınıf adlarını bu listboxlara atıyorum

Örn:
listbox 1 = [ 9-A,9-B,9-C …] (arka planda bu sınıflardan öğrenciler için liste oluşturuyorum)
listbox 2 = [ 10-A, 10-C, 10-C, 10-D]
listbox 3 = [ 11-A, 11-B … ]
listbox 4 = [ 12-A, 12-B, 12-C …] gibi. Aynı gruptakiler karşılaşmayacak. Burada her zaman çalışabilecek bir algoritma geliştirmeye çalışıyorum.

Yine de bunu kontrol eden bir kaç satır kod tutmakta fayda var.

Mantıklı. 9 10 11 ve 12 sınıfları karma dört grup halinde çalışıyorsunuz. Ama bunun yanı sıra 9’un şubeleri ve 10’un şubeleri vs. vs aynı grupta kabul edilip bir arada düşünülebilir.

Sormak istediğim en önemli kısım buydu zaten. Eğer isim bazında çalışıyorsanız size yukarıda önerilen kodu doğrudan kullanamanız normal.

O kod üzerinde liste sıra numaralarının her biri için öğrenci ismini veri tabanınızdan çekecek bir kaç satır da buraya gerekecektir.

Örenğinizde bir diğer soru aklıma geldi. listboxlar içinden sadece bir sınıfı seçerseniz, grubunuz sadece tek sınıftan oluşur. İlk başta sanki aynı sınıfların hepsini bir grup gibi düşünebiliriz gibi gelmişti. Yani son durumda anladığımı teyit etmek için tekrar sorayım. 9 lardan 1 sınıf, 10 lardan bir sınıf 11 lerden ve 12 lerde 1 sınıf olmak üzere aynı gruptan iki öğrenci yan yana gelmeyecek bir liste istiyorsunuz. Bu konuda teyid edersek kod üzerinde çalışabiliriz.

PyQt çalıştığınız bilmemiz, kodunuzun benzerini burada yazdığımızda daha yakın kodlar tasarlamamızı kolaylaştıracaktır.

Soru ve sorununuz biraz daha netleştiğinden çalışmak daha kolay olacaktır.

Veri tabanınız için hangi kütüphaneyi kullandığınızı da yazarsanız daha yakın kodlar deneme şansımız olur.

Bu mesajda da iki soru sormuş oldum o kısımları da netleştirirseniz bir sonuç alabileceğimizi düşünüyorum.

Diğer taraftan konu dışı eğer bunu bir sınav oturma yerleşimi için düşünüyorsanız. Oturma düzeninizi bilirsek daha farklı çözümler de oluşturulabilir. Mesela anfi türü bir yerleşim ile, bir sıraya iki kişinin oturduğu bir oturma düzeninde yan yana gelme olasılıklarını azaltmak daha kolay gibi duruyor.

Önce hepsini tek sıra listeleyelim akabinde bu olasılığı da göz önünde tutmak isterseniz ona da bakabiliriz.

Tüm sınıf isimlerini sınıflar isimli listboxa veritabanından çekiyorum. Buradan 4 lü havuza istediğim gibi ayırıyorum (+,-butonlarına tıklayarak). Sqlite ile çalışıyorum. Bu kısımları yaptım zaten. Bana sadece tüm sayı durumlarında (afaki durumlar hariç) karışım yapabilecek algoritma lazım.

Görseliniz üzerinden gitmeye çalışalım.

Aslında kodu görsek veri tabanınız üzerinden farklı yaklaşımlar da oluşturulabilirdi. Ama madem;

Dediğinize göre bu dört listbox ı doldurabiliyorsunuz. Tabi ne ile doldurduğunuz yine bilemiyoruz. Sormayacağım bu sefer. Varsayımda bulunarak devam edeceğiz.

Dört listemizi sizin farklı uzunlukta öğrenci sınıf listeleri ile doldurduğunuzu varsayıyorum. Ve veri tabanı erişiminizle ilgili bilgi gerekmeden çözebilmek için bu liste kutuları içerisindeki veriler ile işlem yaparak devam edebiliriz.

Öncelikle dört farklı grup için birer listeniz olduğunu ve verdiğiniz koşulları sağlayacak şekilde sıraladığımızı varsayalım:

Yani bu sizin başlığı açarken sorduğunuz ilk kısma anlayabileceğiniz en basit kodlardan biri. Bunu istediğiniz farklı sıralama algoritmaları ile değiştirebilirsiniz.

A = ["1", "2", "3"]
B = ["B"]
C = ["C", "C", "C", "C"]
D = ["D", "D", "D", "D"]

merged = []
max_length = max(len(A), len(B), len(C), len(D))

for i in range(max_length):
    if i < len(A):
        merged.append(A[i])
    if i < len(B):
        merged.append(B[i])
    if i < len(C):
        merged.append(C[i])
    if i < len(D):
        merged.append(D[i])

print(merged)

Bu kodu denediğinizde farklı listeler için listeleri aynı sınıftan öğrenciler yan yana gelmeyecek şekilde karıştırarak tek bir liste halinde alabilirsiniz. Yukarıda katılımcıların da size vereceği listeler benzer sonuçlar döndürecektir.

Ben sadece basit bir örnek yaptım, daha önce belirttiğim üzere, genetik algoritmalar dahil bir çok metodla yapılabilir.

Şimdi bu kodumuzu, dört liste kutusu olan ve beşinci bir kutuya birleştirerek yazan bir koda dönüştürelim.

Bende veri tabanınız liste içindeki isim sınıf okul no gibi bir format olmadığından bazı varsayımlarda bulunacağım. Siz listelerinizde bunları farklı şekilde değiştirebilirsiniz.

Ben bir veri tabanı kullanmadan listeleri random dolduruyorum bu nedenle. Siz random add item yaptığım bölümleri kullanmazsınız, sizinkiler zaten kendi veri tabanınızdan doldurulacaktır.

Ben bunun yerine sınıfı simgelesin diye grup adı olarak A, B, C, D verdim ve bu grup adı yanına da rastgele sayılar ekleyere A-5, C-2 gibi farklı kombinasyonlar oluşturdum. Böylece sınıftaki bir öğrenci ismi ve bir okul numarası ile ilişkilendirilmiş birleşik bir eleman oluşturdum.

Yani sonda size vereceğim kodun büyük bir çoğunluğu, 4 farklı gruba farklı sayıda rastgele bir grup adı ve bir numara dolduruyor. Sizin bunu yapmanıza gerek yok.

                if group_name == "A":
                    self.A.append(group_name+"-"+str(number))
                elif group_name == "B":
                    self.B.append(f"{group_name}-{number}")

Burada iki farklı şekilde, A ve B listelerine listedeki elemanları nasıl ekleyecekseniz onları ekleyebilirsin. Liste item elementleri teker teker kendiniz de alabilirsiniz tecih sizin. Ben append metodunda iki farklı şekilde elemanı buraya yeniden oluşturup ekledim.

Tabi bu kodun bir handikapı var daha önce de belirttim. Eğer herhangi bir sınıf listesi diğer üçünün toplamından büyük ise bu durumda sonda son sınıf elemanları yan yana gelir. Ama siz böyle marjinal bir sınıf sayısı olmadığını belirttiğiniz için üzerinde durmuyorum.

İsterseniz o durumu da kontrol eder en azından çözüm olmadığı konusunda uyarabiliriz.

Rastgele sınıf sayıları üretirken böyle durumlarla karşılaşırsanız birleştirilen son listede yan yana görürsenin bu nedenle şaşırmayın. Sizin sınıflarınız homojen dağınık ise çok böyle bir durumla karışılaşmaya bilirsiniz.

Kodu çalıştırıp bir kaç ekran görüntüsü vereyim öncelikle;

Listeleri rastgele doldur butonuna basıyoruz. Dört listeyi daha önce anlattığım şekilde rasgele dolduruyoruz.

Daha sonra yukarıdaki gibi listeleri birleştir dediğimizde, sınıfları bunu isim soy isim de düşünebilirsiniz ve okul numarasını simgeleyen elemanlar yan yana gelmeyecek şekilde sıralanır.

Söylediğim gibi rastgele liste doldurduğumuzdan, bazan listeler bir veya iki elemanlı olabilir, bu durumda kural nedeniyle çözüm oluşmayabilir ve bazı elemanlar yan yana gelebilir.

Ve baştan beri anlattığım üzere, bu sıralama işlemi için sayısız metod var bir diğer metod ile istediğiniz zaman değiştirebilirsiniz.

Burada kilit konu, A, B, C, D şeklindeki dört liste değişkeninizdir bunları kutulardan doldurup sıraladıktan sonra kutulara birleştirip atan bir örnek üzerinde istediğiniz farklı algoritmaları rahatlıkla kullanırsınız.

Sonuç olarak kodun tümünü de ekleyeyim;

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListWidget, QPushButton
import random


class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Dört Grup Sınıf Örneği")
        self.setGeometry(100, 100, 300, 400)

        layout = QVBoxLayout()

        self.list_widgets = []
        group_names = ["A", "B", "C", "D"]
        for group_name in group_names:
            list_widget = QListWidget()
            list_widget.setObjectName(group_name)  
            layout.addWidget(list_widget)
            self.list_widgets.append(list_widget)

        populate_button = QPushButton("Listeleri Rastgele Doldur")
        populate_button.clicked.connect(self.populate_lists)
        layout.addWidget(populate_button)

        merge_button = QPushButton("Listeleri Birleştir")
        merge_button.clicked.connect(self.merge_lists)
        layout.addWidget(merge_button)

        self.merged_list_widget = QListWidget()
        layout.addWidget(self.merged_list_widget)

        self.setLayout(layout)

        self.A = []
        self.B = []
        self.C = []
        self.D = []

    def populate_lists(self):
        for list_widget in self.list_widgets:
            list_widget.clear()
            group_name = list_widget.objectName()
            count = random.randint(1, 10)  
            for _ in range(count):
                number = random.randint(1, 100)  
                list_widget.addItem(f"{group_name} - {number}")  
                
                if group_name == "A":
                    self.A.append(group_name+"-"+str(number))
                elif group_name == "B":
                    self.B.append(f"{group_name}-{number}")
                elif group_name == "C":
                    self.C.append(f"{group_name}-{number}")
                elif group_name == "D":
                    self.D.append(f"{group_name}-{number}")
                    
        print(self.A)
        print(self.B)
        print(self.C)
        print(self.D)

    def merge_lists(self):
        self.merged_list_widget.clear()
        merged = []
        max_length = max(len(self.A), len(self.B), len(self.C), len(self.D))
        
        for i in range(max_length):
            if i < len(self.A):
                merged.append(self.A[i])
            if i < len(self.B):
                merged.append(self.B[i])
            if i < len(self.C):
                merged.append(self.C[i])
            if i < len(self.D):
                merged.append(self.D[i])
        for item in merged:
            self.merged_list_widget.addItem(str(item))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Umarım bu örnek üzerinden kodunuzu tamamlayabilirsiniz.

Kolay gelsin.

1 Beğeni

Teşekkür ederim ama bunu yaptım zaten. Şu an çalışan kodlanmış sistem bu şekilde. Sırayla her gruptan alıp karışım yapıyorum. Ben fazla kalan gruptaki öğrencileri de araya ekleyebilecek bir algoritma için uğraşıyorum.

Çok uğraşmanız gerektiğini düşünmüyorum.

Basitçe adımlayalım.

Dört grubumuz var. Gruptaki öğrenci sayılarına göre gruplayalım. En uzun grup ana grubumuz olsun, daha sonra bir sonraki gruptan bir öğrenci çekelim. ve ilk en uzun gruptan çektiğimiz ile bireştirim bir listeye ekleyelim. Kısa grup bittiğinde bir sonraki kısa gruba geçerek devam edelim. Eğer en uzun grubumuzun sona geldi ise en uzun grubun tekrar başına dönerek, sıradaki kısa gruplardan eklemeye devam edelim şeklinde basit bir kod ile halledebilirsiniz. Sorun yaşarsanız ona da bakabiliriz.

EDIT 1:

Sizin için basit bir algoritma üzerinde konuşalım:

Elimizde bir listeler grubu olsun:

A = ["A-71", "A-26", "A-84", "A-67","A-68"]
B = ["B-84", "B-7", "B-63", "B-49"]
C = ["C-89", "C-105", "C-110", "C-120"]
D = ["D-63", "D-15", "D-39", "D-31", "D-84", "D-25", "D-91", "D-67", "D-2", "D-71"]

Farklı uzunlukta dört listemiz var.

lists = [A, B, C, D]

for group in lists:
    random.shuffle(group)

Her grubu kendi içerisinde karıştıralım. Böylece her öğrenci sınıf listesindeki sıradan farklı bir konunmda liste içinde karıştırılmış olur. Ama hala aynı gruptan öğrenciler aynı grupta kalmaya devam etmektedir.

Sonra bu dört grubun öğrenci sayılarına göre büyükten küçüğe sıralnamasını sağlayalım.

sorted_lists = sorted(lists, key=lambda x: len(x), reverse=True)

Artık elimizde, kendi içinde rastgele karıştırılmış dört listenin büyükten küçüğe sıralanmış hali var.

Aşağıda bu büyükten küçüğe sıralanmış dört listenin hepsini bir birine birleştirelim:

merged_list = []

for group in sorted_lists:
    merged_list.extend(group)

Yani şu an sadece aynı sınıf öğrencilerinin yan yana yazıldığı ve tüm grupların birleştirildiği bir listemiz var.

Şimdi algoritmanın kilit kısmına geldik. Bu şekli ile öğrenciler yan yana olduğundan istediğim sonuca henüz olaşamadık.

Şimdi bu birleşik listeyi, ortadan ikiye bölelim:

length = len(merged_list)
mid = length // 2

first_half = merged_list[:mid]
second_half = merged_list[mid:]

Artık birleştirilmiş dört gruptan oluşan iki listemiz iki parçaya bölündü.

Sıralı bir iskambil destesi düşünün, sonra bu desteyi ortadan ikiye böldük ve iki parçayı bir sağ deste yarısından bir sol deste yarısından ekleyerek karıştırırsak aynı gruptan yan yana öğrenci kalmayacaktır.

Peki deste tek sayı ise?

Bu durumda bölünmüş büyük destenin arasına küçük olan desteyi birer birer yerleştiriyoruz.

Bunu yapan kod parçasını da ekleyelim:

if len(first_half) >= len(second_half):
    longer_list = first_half
    shorter_list = second_half
else:
    longer_list = second_half
    shorter_list = first_half

merged_list = []

for i in range(len(longer_list)):
    merged_list.append(longer_list[i])
    if i < len(shorter_list):
        merged_list.append(shorter_list[i])

Şimdi, artık listemiz tekrar birleşti ama bu sefer aynı gruptan öğrenciler yan yana olmayacaktır. Aynı zamanda sonda da yan yana artan öğrenci olmayacaktır.

Şimdi birleştirilmiş, karıştırılmış ve aynı grupta öğrencilerin yan yana gelmediğine emin olmak için ekrana çıktısını alalım.

print(merged_list)

Bu durumda kodun tümünü bir kez daha vereyim:

import random

A = ["A-71", "A-26", "A-84", "A-67","A-68"]
B = ["B-84", "B-7", "B-63", "B-49"]
C = ["C-89", "C-105", "C-110", "C-120"]
D = ["D-63", "D-15", "D-39", "D-31", "D-84", "D-25", "D-91", "D-67", "D-2", "D-71"]
###############################################
lists = [A, B, C, D]

for group in lists:
    random.shuffle(group)
###############################################
sorted_lists = sorted(lists, key=lambda x: len(x), reverse=True)

###############################################
merged_list = []

for group in sorted_lists:
    merged_list.extend(group)
    
###############################################

length = len(merged_list)
mid = length // 2

first_half = merged_list[:mid]
second_half = merged_list[mid:]

###############################################

if len(first_half) >= len(second_half):
    longer_list = first_half
    shorter_list = second_half
else:
    longer_list = second_half
    shorter_list = first_half

merged_list = []

for i in range(len(longer_list)):
    merged_list.append(longer_list[i])
    if i < len(shorter_list):
        merged_list.append(shorter_list[i])

print(merged_list)
#################################################

Basit bir algoritma. Okunaklı kalsın diye her adımı ayırdım ve basitleştirdim. Hepsini birleştirip tek bir fonksiyın yada sınıf içine de alabilirsiniz.

Kolay gelsin.

EDIT 2:

Şimdi bu algoritmayı nasıl bir qt arayüzü ile kullanabilirize bakalım.

Koda ufak eklemeler yaptım:

Öncelikle random sınıf mevcutları oluşturmak için minimum 10 max 15 kişilik sınıflar oluşturulmasını sağladım.

count = random.randint(10, 15) 

Satırı ile istediğimiz aralıkta sınıf mevcutlarımız oluştu.

Listeleri rastgele doldur butonuna bastığımızda dört grubun içerisine 10-15 aralığında rastgele sayıda öğrenciler yerleştiriliyor.

Daha sonra bir önceki EDIT’te anladığım karıştıran algoritmayı uygulamak ve alttaki listbox’a yazmdırmak için “Listeleri birleştir” butonuna basıyoruz.

Artık en alttaki liste kutumuzda, karıştırılmış ve bir biri ile aynı gruptan yan yana öğrencilerin gelmediği birleşik listemizi oluşturmuş oluyoruz.

Bu tek listeyi istediğimiz gibi tekrar alt gruplara bölebiliriz.

Dört liste kutumuzun dört sınıf mahali olduğunu ve bu birleşik listediki öğrenciler olabildiğince eşit sayıda bölüp sınıflara yerleştirdiğimizi düşünelim.

Bunun için de;

son olarak “Listeyi Gruplara Yeniden Yerleştir” e basıyoruz. Bu butona bastığımızda liste dört eşit parçaya bölünmeye çalışılır. Aslında numpy kütüphanesi ile daha basit bölünebilirdi ama ben biraz daha dengeli bir bölme için kendim bir parça kod yazım.

Sonuçta butona bastığımızda her sınıf mahaline eşit/eşite yakın sayıda öğrenci bölünerek dağıtılıyor. Tabi önce bu dört sınıf listbox’ını temizliyoruz sonra da böldüğümüz parçaları her bir listeye yerleştiriyoruz.

Dört liste kutusu içerisinde gezindiğinizde her sınıf grubunun içinde aynı sınıftan öğrencilerin yan yana gelmediğini. Aynı zamanda her sınıfta da aşağı yukarı aynı sayıda öğrenci olacak dört sınıfın içine yerleştirdiğini görebiliyoruz.

Umarım bu kod fikir verebilir:

import sys
import random

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListWidget, QPushButton

class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Dört Grup Sınıf Örneği")
        self.setGeometry(100, 100, 300, 400)

        layout = QVBoxLayout()

        self.list_widgets = []
        group_names = ["A", "B", "C", "D"]
        for group_name in group_names:
            list_widget = QListWidget()
            list_widget.setObjectName(group_name)  
            layout.addWidget(list_widget)
            self.list_widgets.append(list_widget)

        populate_button = QPushButton("Listeleri Rastgele Doldur")
        populate_button.clicked.connect(self.populate_lists)
        layout.addWidget(populate_button)

        merge_button = QPushButton("Listeleri Birleştir")
        merge_button.clicked.connect(self.merge_lists)
        layout.addWidget(merge_button)
        
        replace_button = QPushButton("Listeyi Gruplara Yeniden Yerleştir")
        replace_button.clicked.connect(self.distribute_lists)  
        layout.addWidget(replace_button)        

        self.merged_list_widget = QListWidget()
        layout.addWidget(self.merged_list_widget)

        self.setLayout(layout)

        self.A = []
        self.B = []
        self.C = []
        self.D = []
        self.chunks = []

    def populate_lists(self):
        for list_widget in self.list_widgets:
            list_widget.clear()
            group_name = list_widget.objectName()
            count = random.randint(10, 15)  
            for _ in range(count):
                number = random.randint(1, 100)  
                list_widget.addItem(f"{group_name} - {number}")  
                
                if group_name == "A":
                    self.A.append(group_name+"-"+str(number))
                elif group_name == "B":
                    self.B.append(f"{group_name}-{number}")
                elif group_name == "C":
                    self.C.append(f"{group_name}-{number}")
                elif group_name == "D":
                    self.D.append(f"{group_name}-{number}")
                    

    def merge_lists(self):
        self.merged_list_widget.clear()
        
        lists = [self.A, self.B, self.C, self.D]
        for group in lists:
            random.shuffle(group)
        
        sorted_lists = sorted(lists, key=lambda x: len(x), reverse=True)
        
        merged_list = []

        for group in sorted_lists:
            merged_list.extend(group)
            
        length = len(merged_list)
        mid = length // 2
        first_half = merged_list[:mid]
        second_half = merged_list[mid:]    
            
        
        if len(first_half) >= len(second_half):
            longer_list = first_half
            shorter_list = second_half
        else:
            longer_list = second_half
            shorter_list = first_half

        merged_list = []

        for i in range(len(longer_list)):
            merged_list.append(longer_list[i])
            if i < len(shorter_list):
                merged_list.append(shorter_list[i])

               
        for item in merged_list:
            self.merged_list_widget.addItem(str(item))
            
        list_length = len(merged_list)
        chunk_size = list_length // 4
        remainder = list_length % 4

        chunks = []
        start = 0

        for i in range(4):
            if remainder > 0:
                end = start + chunk_size + 1
                remainder -= 1
            else:
                end = start + chunk_size
    
            chunk = merged_list[start:end]
            self.chunks.append(chunk)
    
            start = end
                       
                
    def distribute_lists(self):
        for list_widget in self.list_widgets:
            list_widget.clear()
            
        for count in range(4):
            self.list_widgets[count].addItems(self.chunks[count])
            
      
        
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


Kolay gelsin.

2 Beğeni