Advertisement
lorinczandras

O3 mini hight tesztfeladat

Feb 1st, 2025
618
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 11.85 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2. # invoice_optimizer.rb
  3. #
  4. # Ez az alkalmazás beolvassa a CSV fájlból az számlaütemtervet, majd
  5. # optimalizálja a fizetési dátumokat azzal a céllal, hogy a végső banki egyenleg a lehető legnagyobb legyen.
  6. #
  7. # FONTOS: A kódnál figyeltünk a bemeneti adatok ellenőrzésére és szűrésére.
  8. #        Bár ez nem webalkalmazás, a kód "biztonsági" ellenőrzéseket tartalmaz (pl. fájl ellenőrzés).
  9. #
  10. require 'csv'
  11. require 'date'
  12. require 'optparse'
  13. require 'securerandom'
  14.  
  15. # Bank osztály: tartalmazza a bank paramétereit (kamat, tranzakciós díj stb.)
  16. class Bank
  17.   attr_reader :name, :interest_rate_30_days, :transaction_fee_percentage, :transaction_fee_min, :transaction_fee_max
  18.  
  19.   def initialize(name, interest_rate_30_days, transaction_fee_percentage, transaction_fee_min, transaction_fee_max)
  20.     @name = name
  21.     @interest_rate_30_days = interest_rate_30_days.to_f
  22.     @transaction_fee_percentage = transaction_fee_percentage.to_f
  23.     @transaction_fee_min = transaction_fee_min.to_f
  24.     @transaction_fee_max = transaction_fee_max.to_f
  25.   end
  26.  
  27.   # Számolja ki a tranzakciós díjat adott összegre
  28.   def calculate_transaction_fee(amount)
  29.     fee = amount * @transaction_fee_percentage
  30.     fee = @transaction_fee_min if fee < @transaction_fee_min
  31.     fee = @transaction_fee_max if fee > @transaction_fee_max
  32.     fee
  33.   end
  34.  
  35.   # Napi kamatláb kiszámítása (egyszerű kamatszámítás a 30 napos kamatláb alapján)
  36.   def daily_interest_rate
  37.     @interest_rate_30_days / 30.0
  38.   end
  39. end
  40.  
  41. # Számla (invoice) osztály: tartalmazza a számla adatait
  42. class Invoice
  43.   attr_reader :type, :partner, :bank_name, :gross_amount, :net_amount, :due_date
  44.   attr_accessor :payment_date
  45.  
  46.   def initialize(type, partner, bank_name, gross_amount, net_amount, due_date)
  47.     @type = type.strip
  48.     @partner = partner.strip
  49.     @bank_name = bank_name.strip
  50.     @gross_amount = gross_amount.to_f
  51.     @net_amount = net_amount.to_f
  52.     @due_date = Date.strptime(due_date.strip, '%m/%d/%Y')
  53.     # Alapesetben a fizetés a lejárat napján történik
  54.     @payment_date = @due_date
  55.   end
  56. end
  57.  
  58. # KONFIGURÁCIÓS KONSTANSOK
  59. MAX_EARLY_PAYMENT_DAYS = 10         # Legkorábbi nap, ameddig fizethetünk (inbound számlák esetében)
  60. EARLY_PAYMENT_DISCOUNT_RATE = 0.001   # Napi kedvezmény (pl. 0,1% naponta)
  61. # Globális tranzakciós adó paraméterek
  62. TRANSACTION_TAX_PERCENTAGE = 0.02
  63. TRANSACTION_TAX_MIN = 5.0
  64. TRANSACTION_TAX_MAX = 50.0
  65.  
  66. # Globális bank konfigurációk (példa: Bank1 és Bank2)
  67. BANKS = {
  68.   'Bank1' => Bank.new('Bank1', 0.05, 0.01, 10.0, 100.0),
  69.   'Bank2' => Bank.new('Bank2', 0.04, 0.015, 8.0, 90.0)
  70. }
  71.  
  72. # Számolja ki a globális tranzakciós adót adott összegre
  73. def calculate_transaction_tax(amount)
  74.   tax = amount * TRANSACTION_TAX_PERCENTAGE
  75.   tax = TRANSACTION_TAX_MIN if tax < TRANSACTION_TAX_MIN
  76.   tax = TRANSACTION_TAX_MAX if tax > TRANSACTION_TAX_MAX
  77.   tax
  78. end
  79.  
  80. # Biztonságos fájl beolvasás: ellenőrizzük, hogy a fájl létezik, és a kiterjesztés CSV
  81. def safe_csv_read(file_path)
  82.   unless File.exist?(file_path)
  83.     abort("Hiba: A megadott fájl nem létezik: #{file_path}")
  84.   end
  85.   unless File.extname(file_path).downcase == '.csv'
  86.     abort("Hiba: Csak CSV fájlokat lehet feldolgozni.")
  87.   end
  88.   CSV.read(file_path, headers: true, encoding: 'utf-8')
  89. end
  90.  
  91. # A CSV fájlból beolvassa a számlákat, és létrehoz egy Invoice objektumot minden sorból
  92. def read_invoices(file_path)
  93.   csv_data = safe_csv_read(file_path)
  94.   invoices = []
  95.   csv_data.each do |row|
  96.     # Bemeneti adatok szűrése (trim, ellenőrzés)
  97.     type = row['Típus'] || row['Tipus']
  98.     partner = row['Partner']
  99.     bank_name = row['Számla'] || row['Szamla']
  100.     gross_amount = row['Bruttó összeg'] || row['Brutto osszeg']
  101.     net_amount = row['Nettó összeg'] || row['Netto osszeg']
  102.     due_date = row['Lejárat'] || row['Lejarat']
  103.  
  104.     if type.nil? or partner.nil? or bank_name.nil? or gross_amount.nil? or net_amount.nil? or due_date.nil?
  105.       next  # Ha hiányos az adat, kihagyjuk a sort
  106.     end
  107.  
  108.     invoices << Invoice.new(type, partner, bank_name, gross_amount, net_amount, due_date)
  109.   end
  110.   invoices
  111. end
  112.  
  113. # A szimuláció kiszámolja a végső banki egyenleget a megadott számlaütemterv alapján.
  114. # A pénzmozgás naponta történik, és minden bank esetében:
  115. # - Az outbound számlák (kimenő) bevételt jelentenek, melyek a due_date-n kerülnek letétbe.
  116. # - Az inbound számlák (bejövő) kifizetésként szerepelnek a fizetési dátumukon,
  117. #   csoportosítva partner szerint, így az egy napra eső fizetésekre a banki tranzakciós díj
  118. #   a teljes összeg alapján kerül kiszámolásra.
  119. # - A fizetett számlák esetében a korai fizetés kedvezménnyel jár (napi szinten számolva).
  120. def simulate_cash_flow(invoices)
  121.   # Szétválasztjuk az inbound és outbound számlákat
  122.   inbound_invoices = invoices.select { |inv| inv.type.downcase.include?('bejövő') }
  123.   outbound_invoices = invoices.select { |inv| inv.type.downcase.include?('kimenő') }
  124.  
  125.   # Időtartam meghatározása: a legkorábbi esemény napjától a legkésőbb esemény napjáig
  126.   all_dates = []
  127.   inbound_invoices.each { |inv| all_dates << inv.payment_date; all_dates << inv.due_date }
  128.   outbound_invoices.each { |inv| all_dates << inv.due_date }
  129.   start_date = all_dates.min
  130.   end_date = all_dates.max
  131.  
  132.   # Bank egyenlegek inicializálása (minden banknál 0-ról indulunk)
  133.   bank_balances = {}
  134.   BANKS.each_key do |bank_name|
  135.     bank_balances[bank_name] = 0.0
  136.   end
  137.  
  138.   # Események csoportosítása: minden napra minden banknál
  139.   # events[nap][bank] = { deposits: [kimenő számlák], payments: { partner => [bejövő számlák] } }
  140.   events = {}
  141.   (start_date..end_date).each do |date|
  142.     events[date] = {}
  143.     BANKS.each_key do |bank_name|
  144.       events[date][bank_name] = { deposits: [], payments: {} }
  145.     end
  146.   end
  147.  
  148.   # Outbound számlák: a due_date-n letétbe kerülnek (bevétel)
  149.   outbound_invoices.each do |inv|
  150.     bank = inv.bank_name
  151.     date = inv.due_date
  152.     if events.key?(date) and events[date].key?(bank)
  153.       events[date][bank][:deposits] << inv
  154.     end
  155.   end
  156.  
  157.   # Inbound számlák: a fizetés a payment_date-n történik
  158.   inbound_invoices.each do |inv|
  159.     bank = inv.bank_name
  160.     date = inv.payment_date
  161.     if events.key?(date) and events[date].key?(bank)
  162.       events[date][bank][:payments][inv.partner] ||= []
  163.       events[date][bank][:payments][inv.partner] << inv
  164.     end
  165.   end
  166.  
  167.   # Napi szimuláció: minden nap végigmegyünk a pénzmozgáson
  168.   current_date = start_date
  169.   while current_date <= end_date
  170.     BANKS.each do |bank_name, bank_obj|
  171.       # 1. Letétek: hozzáadjuk a bank egyenlegéhez a kimenő számlák nettó összegét
  172.       if events[current_date][bank_name][:deposits].any?
  173.         events[current_date][bank_name][:deposits].each do |inv|
  174.           bank_balances[bank_name] += inv.net_amount  # Feltételezzük, hogy ezek pozitív értékűek
  175.         end
  176.       end
  177.  
  178.       # 2. Kifizetések: inbound számlák fizetése, csoportosítva partner szerint
  179.       if events[current_date][bank_name][:payments].any?
  180.         events[current_date][bank_name][:payments].each do |partner, inv_list|
  181.           total_payment = 0.0
  182.           inv_list.each do |inv|
  183.             # Az alap kifizetendő összeg: a számla nettó összegének abszolút értéke
  184.             base_amount = inv.net_amount.abs
  185.             # Számoljuk ki, hány nappal fizetünk korábban a lejárathoz képest
  186.             days_early = (inv.due_date - inv.payment_date).to_i
  187.             discount = days_early * EARLY_PAYMENT_DISCOUNT_RATE * base_amount
  188.             effective_payment = base_amount - discount
  189.             total_payment += effective_payment
  190.           end
  191.           # Banki tranzakciós díj számítása
  192.           fee = bank_obj.calculate_transaction_fee(total_payment)
  193.           # Globális tranzakciós adó
  194.           tax = calculate_transaction_tax(total_payment)
  195.           total_outflow = total_payment + fee + tax
  196.           bank_balances[bank_name] -= total_outflow
  197.         end
  198.       end
  199.  
  200.       # 3. Napi kamat, ha pozitív a bank egyenleg
  201.       if bank_balances[bank_name] > 0
  202.         bank_balances[bank_name] += bank_balances[bank_name] * bank_obj.daily_interest_rate
  203.       end
  204.     end
  205.     current_date += 1
  206.   end
  207.  
  208.   # Összegezzük az összes bank egyenlegét
  209.   total_balance = bank_balances.values.sum
  210.   total_balance
  211. end
  212.  
  213. # Véletlenszerűen módosítja egy inbound számla fizetési dátumát az engedélyezett intervallumon belül
  214. def random_modify_schedule(invoices)
  215.   # Csak a bejövő számlákat vesszük figyelembe
  216.   inbound_invoices = invoices.select { |inv| inv.type.downcase.include?('bejövő') }
  217.   return if inbound_invoices.empty?
  218.   inv = inbound_invoices.sample
  219.   # Engedélyezett intervallum: [lejárat - MAX_EARLY_PAYMENT_DAYS, lejárat]
  220.   earliest = inv.due_date - MAX_EARLY_PAYMENT_DAYS
  221.   latest = inv.due_date
  222.   range = (earliest..latest).to_a
  223.   inv.payment_date = range.sample
  224. end
  225.  
  226. # Mélyklónozza a számlák aktuális ütemtervét (így módosítások esetén nem módosítjuk az eredetit)
  227. def clone_schedule(invoices)
  228.   invoices.map do |inv|
  229.     cloned = Invoice.new(inv.type, inv.partner, inv.bank_name, inv.gross_amount, inv.net_amount, inv.due_date.strftime('%m/%d/%Y'))
  230.     cloned.payment_date = inv.payment_date
  231.     cloned
  232.   end
  233. end
  234.  
  235. # Optimalizálja a számlaütemtervet iteratív (véletlenszerű keresés alapú) módon,
  236. # hogy a végső banki egyenleg a lehető legnagyobb legyen.
  237. def optimize_schedule(invoices, iterations)
  238.   best_invoices = clone_schedule(invoices)
  239.   best_score = simulate_cash_flow(best_invoices)
  240.   current_invoices = clone_schedule(invoices)
  241.   current_score = best_score
  242.  
  243.   iterations.times do |i|
  244.     # Véletlenszerű módosítás egy számla fizetési dátumán
  245.     new_invoices = clone_schedule(current_invoices)
  246.     random_modify_schedule(new_invoices)
  247.     new_score = simulate_cash_flow(new_invoices)
  248.     # Ha javulást látunk, elfogadjuk a módosítást
  249.     if new_score > current_score
  250.       current_invoices = new_invoices
  251.       current_score = new_score
  252.       # Ha ez a legjobb eddigi eredmény, frissítjük a best_invoices változót
  253.       if current_score > best_score
  254.         best_invoices = clone_schedule(current_invoices)
  255.         best_score = current_score
  256.       end
  257.     end
  258.  
  259.     # Időközben jelenítsük meg a futás előrehaladását (ha lehetséges)
  260.     if (i+1) % (iterations/100) == 0
  261.       progress = ((i+1).to_f / iterations.to_f * 100).round(2)
  262.       puts "Haladás: #{progress} %"
  263.     end
  264.   end
  265.  
  266.   return best_invoices, best_score
  267. end
  268.  
  269. # FŐPROGRAM
  270. if __FILE__ == $0
  271.   # Parancssori argumentumok feldolgozása
  272.   options = {}
  273.   OptionParser.new do |opts|
  274.     opts.banner = "Használat: ruby invoice_optimizer.rb [opciók] CSV_FÁJL"
  275.     opts.on("-i", "--iterations ITERATIONS", Integer, "Iterációk száma (alapértelmezett: 10000)") do |it|
  276.       options[:iterations] = it
  277.     end
  278.   end.parse!
  279.  
  280.   if ARGV.empty?
  281.     abort("Hiba: Kérlek add meg a CSV fájl elérési útját!")
  282.   end
  283.  
  284.   csv_file = ARGV[0]
  285.   iterations = options[:iterations] || 10000
  286.  
  287.   # Számlák beolvasása
  288.   invoices = read_invoices(csv_file)
  289.   if invoices.empty?
  290.     abort("Hiba: Nem sikerült számlákat beolvasni a fájlból!")
  291.   end
  292.  
  293.   puts "Számlák beolvasva: #{invoices.size} számla."
  294.  
  295.   # Ütemterv optimalizálása
  296.   best_schedule, best_score = optimize_schedule(invoices, iterations)
  297.   puts "\nOptimális végső bank egyenleg: #{best_score.round(2)}"
  298.  
  299.   # Az optimalizált ütemterv megjelenítése: inbound számlák fizetési dátumai
  300.   puts "\nOptimalizált fizetési dátumok (bejövő számlák):"
  301.   best_schedule.each do |inv|
  302.     if inv.type.downcase.include?('bejövő')
  303.       puts "Partner: #{inv.partner}, Eredeti lejárat: #{inv.due_date}, Fizetési dátum: #{inv.payment_date}"
  304.     end
  305.   end
  306. end
  307.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement