1
\$\begingroup\$

Let's say you have a hashmap of sub-systems that a spaceship is currently directing power to and a second hash of sub-systems where the power should now be directed to.

Create a function that returns a hash of where the spaceship should move the power from and where it should go so that the power can be taken from the current values to the desired ones.

Example:

Current: {engines:500, cannons:150, communications:250, life_support:200, guidance:200}
Desired: {engines:450, entertainment:400, guidance:200, life_support:250}

This should produce:

{
  engines: {entertainment: 50},
  cannons: {entertainment: 150},
  communications: {entertainment: 200, life_support: 50}
}

Assumptions: The sum of the values of the current and the desired hashes is the same.

This is the code I wrote that completes the task:

current = {engines:500, cannons:150, communications:250, life_support:200, guidance:200}
desired = {engines:450, entertainment:400, guidance:200, life_support:250}

def direct_power(current, desired)
  additions = {}
  subtractions = {}
  current.each do |ticker, value|
    movement = (desired[ticker].nil? ? 0 : desired[ticker]) - value
    movement < 0 ? subtractions[ticker] = movement : additions[ticker] = movement
  end
  
  desired.each do |desired_ticker, desired_value|
    additions[desired_ticker] = desired_value if current[desired_ticker].nil? 
  end
  
  additions = Hash[additions.sort_by{|k, v| v}.reverse]
  subtractions = Hash[subtractions.sort_by{|k, v| v}.reverse]
    
  transactions = {}
  subtractions.each do |ticker, value|
    additions.each do |add_ticker, add_value|
      next if add_value.zero? || value.zero?
      
      transactions[ticker] = {} if transactions[ticker].nil?
      if value.abs() < additions[add_ticker]
        transactions[ticker][add_ticker] = value.abs()
        additions[add_ticker] = additions[add_ticker] + value
        value = 0
      elsif value.abs() == additions[add_ticker]
        transactions[ticker][add_ticker] = additions[add_ticker]
        additions[add_ticker] = 0
        value = 0
      else
        transactions[ticker][add_ticker] = additions[add_ticker]
        value = additions[add_ticker] + value
        additions[add_ticker] = 0
      end
    end
  end 
end

direct_power(current, desired)

I think this is currently running N squared, Do you think this be improved in terms of performance, readability and storage, if so how?

\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

Assumptions

Assumptions: The sum of the values of the current and the desired hashes is the same.

Assumptions don't exist. If it is not defined in code then nothing can be assumed about it. If basic definitions are missing then at least some program requirements are missing. Now the program design and code structure are suspect.

... current and the desired hashes ...

Scotty: "we don't have the power, Captain!"

Kirk: "Spock! Add hash!".

Spock: "the hashes are not in equilibrium but I am sanguine Captain."

Kirk: "Scotty! Hash the dilithium crystals or we're all going to die!"

Spock: `"we reached equilibrium hashing Captain but tell that idiot engineer to use all available hash!"

Definitions

  • Maximum Power: ship total power output possible
    • This is what the equality assumption really is
    • Here is the root problem. Write the program referencing "power" and define (declare a constant) that power explicitly.
  • Commanded Power, Current Power, Relative Power ...
    • Now write code using "power" to abstract the hashes
  • Put the implementation details in the engine room for the red shirts to worry about.

Tech Talk

Having the current and desired hashes "be equal" does not account for the amount of total power available.

Test current, desired for equality at the very beginning. If equal then directly overwrite current with desired values.

If there is a hash-member-value power allocation algorithm in there I can't see it. If there is a rudimentary priority then a hash of system priority (priority defined) will help.

The code is impossible to understand. I'm looking for clues that power is being adjusted. ticker, transaction, value ... Name things for what they are not the implementation.

Create the helper hashes up front with keys initialized to zero.

Label the "end end end end" cascade w/ matching if, at least every other one.

The Algorithm

This is troubling:

if value.abs() < additions[add_ticker]

It ensures one value is positive but assumes the other is positive.

The power-change concept needs homogeneity.

It seems that "PowerChange" is ASSUMED TO BE absolute to:from values. Yet sometimes one or the other value is made negative and BOOM! we make this one a relative change.

Actual power changes should be handled the same. It's a power change, period. Given that certainty means there are not separate structures (additions/subtractions). "Transactions" nuances may alter replace a given power-change command but in the final analysis the PowerChange data, structures, and use is consistent.

In other words....

SEPARATION OF CONCERNS, SINGLE RESPONSIBILITY PRINCIPLE!

Do not decide to alter the commanded-power in the middle of a power change. Decide what to set, change replace it if necessary then make the power-change.

Simulating power-change results must not on a whim temporarily redefine the meaning, and use of PowerChange data and internal structures.

Simple Objects

This is just beyond your immediate concerns because of the refactoring it implies.

This is pseudo code!

class PowerState
   @@MAX_POWER = 314159
   @@SHIP_SYSTEMS = { engines: 0, life_support: 0 ... }

   attr_reader :systems 

   def initialize ( initialSettings )
    systems = Hash[@@SHIP_SYSTEMS] 
    update (initialSettings)

    explode if !in_limits?
   end

   def consumption  
       # sum of systems values
   end

   def in_limits?
     consumption <= @@MAX_POWER
   end

   def change ( theseSystems )  # add/subtract TBD
     newSettings = PowerState.new ( this.systems )
     newSettings.update (theseSystems)
     this.update(theseSystems) if newSettings.in_limits?
   end

   def update(theseSystems)   
     # blindly apply settings
   end 
end #PowerState
\$\endgroup\$
2
  • \$\begingroup\$ I really like some of the ideas you bring. The purpose of the code is to allocate the power transactionally one sub-system at a time, i.e: it should be shown where the power is being redirected from/to. That shoul be easy to add. I'll rework my code using an OO patter like you present \$\endgroup\$
    – Nelo
    Commented Jun 28, 2023 at 8:12
  • 1
    \$\begingroup\$ O, I C. I've added `The Algorithm" section to my answer. \$\endgroup\$
    – radarbob
    Commented Jun 28, 2023 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.