Lua Table Sorting 2 compares - sorting

I have gone over as many of the answers here as I could find that had titles I considered near enough to my problem to look into. I haven't seen anyone having my exact issue, so I'm asking a question I hope is just me being ignorant to a simple fact.
I'm trying to code a table that records HP (int) and the distance (boolean) and then sort by HP with only the ones in Range near the top.
local tableTest = {
{hp = 64, range = true, name="Frank"},
{hp = 100, range = true, name="Joe"},
{hp = 2, range = false, name="Jim"},
{hp = 76, range = true, name="Tim"},
{hp = 17, range = false, name="Jill"},
{hp = 16, range = true, name="Phillip"},
}
-- Sort by HP and Range to find lowest Unit in Range.
table.sort(tableTest, function(x,y) return x.hp < y.hp and x.range end)
for i=1, #tableTest do print(tableTest[i].name, tableTest[i].hp) end
The output for this is:
Phillip 16
Jim 2
Frank 64
Jill 17
Tim 76
Joe 100
The output I was expecting from this would be:
Phillip 16
Frank 64
Tim 76
Joe 100
Jim 2
Jill 17
I pray this is just a misunderstanding on my part of how the table.sort works with multiple checks like this (I assumed it was closer to how you declare a variable like this).
edit
Additional information - If I change the order of where the range=false indexes are located in the table, the output changes as well (still incorrect). The values just sort themselves into different indexes after the sort.

According to your description, your order function needs to compare range first, then compare hp.
table.sort(tableTest, function(x,y)
if x.range and y.range then return x.hp < y.hp
elseif x.range then return true
elseif y.range then return false
else return x.hp < y.hp end
end)
Maybe there is some shorter version, but this one sure works and the logic is clear.

You already got an answer to this question but I think it's worth adding another one here that covers how you can reason about this logic easier. The ideas presented here are really language agnostic.
The purpose of providing a comparison function is really to answer one question: Should x come before y? Another way to ask the same question is, does x have a higher precedence then y? Often you implement this with the same ordering properties as say the < operator.
So your function should return true only if x definitely precedes y. In your case, you are really sorting by the range field first and if they both happen to be true then use the hp field as a "tie-breaker".
You can construct a truth table here to help you find the most concise way to express the logical condition that yields the behavior you're looking for:
x | y | x before y?
-------------------------
T | T | x.hp < y.hp
T | F | T
F | T | F
F | F | F
Your original condition x.hp < y.hp and x.range is close but not quite correct for all possible cases.
Above we see that if x is false then the final outcome is also false regardless of what y is. So y is only considered when x is true. Lastly, to avoid the caveat of a falsey condition in the logic short-circuit in lua we'll want x.hp < y.hp to be at the end of the logical expression. Thus, the logical condition you're looking for is:
return x.range and (not y.range or x.hp < y.hp)

Related

Check if numeral within bounds in one statement

Is there a way to check if a number x is greater than a number a and less than a number b, without specifying x twice?
I can do this:
x = 4
a = 1
b = 10
x > a && x < b
...but is there a way to do something like this:
# not valid ruby, is there another way?
a < x < b
This does not answer the question exactly, please read the Edit part of the answer.
Tested this to work for x being an Integer and a Float.
(a..b).include? x
Edit:
As #spickermann pointed out in comments the original question excludes the beginning and the end of the interval.
To exclude the end is easy, this is what ... is for. Therefore
(a...b).include? x # is the same as `a <= x && x < b`
To exclude the start of the interval is not that easy. One could use next_float:
(a.to_f.next_float...b).include? x
However even the (a...b) is something I would not use because it is not so common literal and in my opinion decreases readability of the code. With a.to_f.next_float we are making some really awkward code that may work for Integers and Floats but I would be afraid of other Numeric data types.
Unless someone brings completely different approach I would stick with x > a && x < b

Decision table to pseudocode

Create a decision table to help the Municipal Bank decide whether or not to loan money to a customer. Include the criteria used by the bank to identify qualified applicants.
Conditions
Income >=40000? T T T F F F
Credit Score >=600? T F F T T F
Months at job > 12? - T F T F -
Outcomes
Approve loan? Y Y X Y X X
Use pseudo code to write an algorithm for the decision table created in question one.
If Income >= 4000 And credScore >= 600 And monthJob > 12 Then
loanApp = Yes
I am having trouble converting the table to pseudo code, I wanted to know if the partial answer to the second question is on the right track.
Generally the approach works, yes. Although if you look carefully, you will see that monthJob > 12 is not needed for the first column of your condiditions. (there is a '-' there)
A faster approach you can get if you investigate your conditions. We always get Y if 2 of the conditions are met, otherwise we get X.
So here a optimised version (pseudocode):
score = 0
If Income >= 40000 Then
score = score + 1
Endif
If credScore >= 600 Then
score = score + 1
Endif
If monthJob > 12 Then
score = score + 1
Endif
If score >= 2 Then
loanApp = Yes
Else
loanApp = No
EndIf

Can we write an algorithm which gives me two whole numbers X and Y when I want to get a desired fraction F such that F= X/Y?

I am working to prepare a test data set in which I have to check rounding. Suppose I want to check round, round_up and round_down is working correctly at 10 th decimal place or not.
Then
if, X=100 and Y = 54 so, X/Y = 1.8518518518518518518518518518519 (test round equidistant)
if, X= 10 and Y = 7 so, 1.4285714285714285714285714285714 (test round_up)
if, X= 10 and Y = 3 so, 3.3333333333333333333333333333333 (test round_down)
Can we write an algorithm in which
input will be rounding mode (round_up, round, round_down) and decimal place I want to round at(in our example 10)
output will be X and Y like above?
If the required location is p (=10 in your example), then y=10^p and then you can choose any x you want.
Depending on the language you are using, p might be too big for you to do 10^p, so in the worst case just divide the result from x/y by 10, 100 or whatever is necessary.
Or you can do like this
# n = number of fraction you want to return
def getFraction(a, b, n):
result = ""
for i in range(n):
f = int((a % b) * 10 / b)
result += str(f)
a = a * 10 - b * f
return result
getFraction(10, 7, 11) # return 42857142857 which 10/7 = 1.42857142857...
What I do is like what you have learnt in elementary school on how to do division by pen and paper.
Actually, if the required digit is d, then if d is not 9, the answer would be x=d,y=9 regardless of p which is the position of the digit. If d is 9, then if p is odd, the answer is x=10,y=11 and if p is even, x=1,y=11. If a trivial answer for d=0 won't do, the mirror answer for d=9 is suitable, that is, if d=0 and p is odd, the answer is x=1,y=11, and if p is even, x=10,y=11. A lot shorter than an answer with y=10^p and certainly fitting in nearly any architecture.

questions on binary search in Programming Pearls, 2nd Edition

I am reading Section 9.3 of Jon Bentley's Programming Pearls, 2nd Edition.
On page 94, Jon gave an implementation of an improved binary search algorithm, utilizing the fact that n is 1000 (search 1000 numbers to find the target).
At the end of the program, it is:
if p > 1000 || x[p] != t
p = -1
My question is that, what if p is exactly 1000? It seems like the when p is 1000, it should also error out, like:
if p >= 1000 || x[p] != t
p = -1
Anyway, this part of code is translated from the code on page 93, at the end of wich:
if p >= n || x[p] != t
p = -1
Is my understanding correct? I am just wondering if this is a typo, or it is really not necessary to include the case p is 1000 in the condtion.
Another question is, in lines 5~6 from bottom up on page 94, it says: When the first test fails and l stays zero, the program computes the bits of p in order, most significant bit first.
What does it mean here? And when the first test fails, shoudn't l be -1, rather than 0?
Anyone can elaborate on this statement?
P.S. I can't find Jon's email address, otherwise, I'll field these questions to him. :-(
It is typo. Maxvalue of l is 999 (1000 - 512 + 256 + .. + 1, ), so maxvalue of p = l+1 is 1000. It is clear for hardcoded version of binsearch (listing 9.8).
And we can see real C code (not pseudocode) here (Alg.4) with if (p >= n ||

Sorting a string that could contain either a time or distance

I have implemented a sorting algorithm for a custom string that represents either time or distance data for track & field events. Below is the format
'10:03.00 - Either ten minutes and three seconds or 10 feet, three inches
The result of the sort is that for field events, the longest throw or jump would be the first element while for running events, the fastest time would be first. Below is the code I am currently using for field events. I didn't post the running_event_sort since it is the same logic with the greater than/less than swapped. While it works, it just seems overly complex and needs to be refactored. I am open to suggestions. Any help would be great.
event_participants.sort!{ |a, b| Participant.field_event_sort(a, b) }
class Participant
def self.field_event_sort(a, b)
a_parts = a.time_distance.scan(/'([\d]*):([\d]*).([\d]*)/)
b_parts = b.time_distance.scan(/'([\d]*):([\d]*).([\d]*)/)
if(a_parts.empty? || b_parts.empty?)
0
elsif a_parts[0][0] == b_parts[0][0]
if a_parts[0][1] == b_parts[0][1]
if a_parts[0][2] > b_parts[0][2]
-1
elsif a_parts[0][2] < b_parts[0][2]
1
else
0
end
elsif a_parts[0][1] > b_parts[0][1]
-1
else
1
end
elsif a_parts[0][0] > b_parts[0][0]
-1
else
1
end
end
end
This is a situation where #sort_by could simplify your code enormously:
event_participants = event_participants.sort_by do |s|
if s =~ /'(\d+):(\d+)\.(\d+)/
[ $1, $2, $3 ].map { |digits| digits.to_i }
else
[]
end
end.reverse
Here, I parse the relevant times into an array of integers, and use those as a sorting key for the data. Array comparisons are done entry by entry, with the first being the most significant, so this works well.
One thing you don't do is convert the digits to integers, which you most likely want to do. Otherwise, you'll have issues with "100" < "2" #=> true. This is why I added the #map step.
Also, in your regex, the square brackets around \d are unnecessary, though you do want to escape the period so it doesn't match all characters.
One way the code I gave doesn't match the code you gave is in the situation where a line doesn't contain any distances. Your code will compare them as equal to surrounding lines (which may get you into trouble if the sorting algorithm assumes equality is transitive. That is a == b, b == c implies a ==c, which is not the case for your code : for example a = "'10:00.1", b = "frog", c="'9:99:9").
#sort_by sorts in ascending order, so the call to #reverse will change it into descending order. #sort_by also has the advantage of only parsing out the comparison values once, whereas your algorithm will have to parse each line for every comparison.
Instead of implementing the sort like this, maybe you should have a TrackTime and FieldDistance models. They don't necessarily need to be persisted - the Participant
model could create them from time_distance when it is loaded.
You're probably going to want to be able to get the difference between two values, validate values as well sort values in the future. The model would make it easy to add these features. Also it would make unit testing a lot easier.
I'd also separate time and distance into two separate fields. Having dual purpose columns in the database only causes pain down the line in my experience.
I don't know ruby but here's some c-like pseudo code that refactors this a bit.
/// In c, I would probably shorten this with the ? operator.
int compareIntegers(a, b) {
int result = 0;
if (a < b) {
result = -1;
} else if (a > b) {
result = 1;
}
return result;
}
int compareValues(a, b) {
int result = 0;
if (!/* check for empty*/) {
int majorA = /* part before first colon */
int majorB = /* part before first colon */
int minorA = /* part after first colon */
int minorB = /* part after first colon */
/// In c, I would probably shorten this with the ? operator.
result = compareIntegers(majorA, majorB);
if (result == 0) {
result = compareIntegers(minorA, minorB);
}
}
return result;
}
Your routine looks fine but you could just remove the ''', ':' and '.' and treat the result as a numeric string. In other words the 10' 5" would become 1005 and 10' 4" would be 1004. 1005 is clearly more than 1004.
Since the higer order elements are on the left, it will sort naturally. This also works with time for the same reasons.
I agree that converting to integers will make is simpler. Also note that for integers
if a > b
1
elsif a < b
-1
else
0
can be simplified to a<=>b. To get the reverse use -(a <=> b).
In this scenario:
Since you know you are working with feet, inches, and (whatever your third unit of measure is), why not just create a total sum of the two values you are comparing?
So after these two lines:
a_parts = a.time_distance.scan(/'([\d]):([\d]).([\d])/)
b_parts = b.time_distance.scan(/'([\d]):([\d]).([\d])/)
Generate the total distance for a_parts and b_parts:
totalDistanceA = a_parts[0][0].to_i * 12 + a_parts[0][1].to_i + b_parts[0][2].to_i * (whatever your third unit of measure factor against the size of an inch)
totalDistanceB = b_parts[0][0].to_i * 12 + b_parts[0][1].to_i + b_parts[0][2].to_i * (whatever your third unit of measure factor against the size of an inch)
Then return the comparison of these two values:
totalDistanceA <=> totalDistanceB
Note that you should keep the validation you are already making that checks if a_parts and b_parts are empty or not:
a_parts.empty? || b_parts.empty?
For doing the sorting by time scenario, do the exact same thing except with different factors (for example, 60 seconds to a min).
Why not do
a_val = a_parts[0][0].to_i * 10000 + a_parts[0][1].to_i * 100 + a_parts[0][2].to_i
b_val = b_parts[0][0].to_i * 10000 + b_parts[0][1].to_i * 100 + b_parts[0][2].to_i
a_val <=> b_val
The numbers won't make sense to subtract, etc but they should sort ok.
You may want to check [1] and [2] are always two digits in the regexp.

Resources