permutations of N balls in M boxes in C++ - c++

I asked a question last week about permutations in C++ (List of combinations of N balls in M boxes in C++).
The answers have helped me a lot but my problem has now changed.
What i would like to do is a translation from this python function to C++, keeping the same order in the result :
def combinations_with_replacement_counts(n, r): #(n-boxes, r-balls)
size = n + r - 1
for indices in itertools.combinations(range(size), n-1):
#print indices
starts = [0] + [index+1 for index in indices]
stops = indices + (size,)
yield tuple(map(operator.sub, stops, starts))
I've no skill in python and despite my readings of the doc, I don't understand this function.

You know python is interpreted, right? You can just type the lines you don't understand directly into python and see what happens ... start with small values first.
I don't understand the itertools.combinations() algorithm
The documentation is here, and includes example output.
Note that the value returned from combinations is lazy, so you need to force evaluation to see it:
>>> import itertools
>>> itertools.combinations(range(4), 2)
<itertools.combinations object at 0x979a964>
>>> list(itertools.combinations(range(4), 2))
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
Is it clear what combinations does now? If not, have a play with it.
... and the syntax of "stops = indices + (size,)"
So try it, it won't bite:
>>> indices=list(itertools.combinations(range(4), 2))[0]
>>> size=4
>>> stops=indices + (size,)
>>> indices
(0, 1)
>>> stops
(0, 1, 4)
The syntax (x,) creates a one-element tuple (an invariant sequence - just like a list you can't change but with round parentheses () instead of square ones []). You can use [x] to create a one-element list, but (x) would be ambiguous since round parentheses are also used for other things, like function arguments and grouping.
Concerning map(), ...
Read the doc, have a play with it, it isn't hard.

This C++ code seems to have the same results as your python sample. It is far from the perfect one, still you could understand the algorithm and even use this implementation.
#include <deque>
typedef std::deque<size_t> BoxList;
class Generator {
size_t boxNum, ballNum, ownBox;
Generator* recursive;
public:
~Generator() { if ( recursive == NULL ) delete recursive; }
Generator( size_t boxes, size_t balls ) : boxNum(boxes), ballNum(balls) {
if ( boxes > 1 ) {
recursive = new Generator( boxes-1, balls );
ownBox = 0;
} else {
recursive = NULL;
ownBox = balls;
}
}
BoxList operator()() {
if ( ownBox > ballNum ) throw 1;
if ( boxNum <= 1 ) return BoxList( 1, ownBox++ );
try {
BoxList res = recursive->operator()();
res.push_front( ownBox );
return res;
}
catch(...) {
delete recursive;
ownBox++;
recursive = new Generator( boxNum-1, ballNum-ownBox );
return operator()();
}
}
};
Class interface allows you to use it as a standard generator. Operator () will generate exception when all possible options have been already iterated.
Generator g( boxes, balls );
try{
while( true )
g();
}
catch(...) {}

The Python code you quoted is implementing the algorithm I described in my answer to your question: it's iterating over the possible ways to place r − 1 boxes in n + r − 1 positions, and then making a list of the differences between adjacent positions (which count the number of balls that are swept into that box).

Related

How to store output of very large Fibonacci number?

I am making a program for nth Fibonacci number. I made the following program using recursion and memoization.
The main problem is that the value of n can go up to 10000 which means that the Fibonacci number of 10000 would be more than 2000 digit long.
With a little bit of googling, I found that i could use arrays and store every digit of the solution in an element of the array but I am still not able to figure out how to implement this approach with my program.
#include<iostream>
using namespace std;
long long int memo[101000];
long long int n;
long long int fib(long long int n)
{
if(n==1 || n==2)
return 1;
if(memo[n]!=0)
return memo[n];
return memo[n] = fib(n-1) + fib(n-2);
}
int main()
{
cin>>n;
long long int ans = fib(n);
cout<<ans;
}
How do I implement that approach or if there is another method that can be used to achieve such large values?
One thing that I think should be pointed out is there's other ways to implement fib that are much easier for something like C++ to compute
consider the following pseudo code
function fib (n) {
let a = 0, b = 1, _;
while (n > 0) {
_ = a;
a = b;
b = b + _;
n = n - 1;
}
return a;
}
This doesn't require memoisation and you don't have to be concerned about blowing up your stack with too many recursive calls. Recursion is a really powerful looping construct but it's one of those fubu things that's best left to langs like Lisp, Scheme, Kotlin, Lua (and a few others) that support it so elegantly.
That's not to say tail call elimination is impossible in C++, but unless you're doing something to optimise/compile for it explicitly, I'm doubtful that whatever compiler you're using would support it by default.
As for computing the exceptionally large numbers, you'll have to either get creative doing adding The Hard Way or rely upon an arbitrary precision arithmetic library like GMP. I'm sure there's other libs for this too.
Adding The Hard Way™
Remember how you used to add big numbers when you were a little tater tot, fresh off the aluminum foil?
5-year-old math
1259601512351095520986368
+ 50695640938240596831104
---------------------------
?
Well you gotta add each column, right to left. And when a column overflows into the double digits, remember to carry that 1 over to the next column.
... <-001
1259601512351095520986368
+ 50695640938240596831104
---------------------------
... <-472
The 10,000th fibonacci number is thousands of digits long, so there's no way that's going to fit in any integer C++ provides out of the box. So without relying upon a library, you could use a string or an array of single-digit numbers. To output the final number, you'll have to convert it to a string tho.
(woflram alpha: fibonacci 10000)
Doing it this way, you'll perform a couple million single-digit additions; it might take a while, but it should be a breeze for any modern computer to handle. Time to get to work !
Here's an example in of a Bignum module in JavaScript
const Bignum =
{ fromInt: (n = 0) =>
n < 10
? [ n ]
: [ n % 10, ...Bignum.fromInt (n / 10 >> 0) ]
, fromString: (s = "0") =>
Array.from (s, Number) .reverse ()
, toString: (b) =>
b .reverse () .join ("")
, add: (b1, b2) =>
{
const len = Math.max (b1.length, b2.length)
let answer = []
let carry = 0
for (let i = 0; i < len; i = i + 1) {
const x = b1[i] || 0
const y = b2[i] || 0
const sum = x + y + carry
answer.push (sum % 10)
carry = sum / 10 >> 0
}
if (carry > 0) answer.push (carry)
return answer
}
}
We can verify that the Wolfram Alpha answer above is correct
const { fromInt, toString, add } =
Bignum
const bigfib = (n = 0) =>
{
let a = fromInt (0)
let b = fromInt (1)
let _
while (n > 0) {
_ = a
a = b
b = add (b, _)
n = n - 1
}
return toString (a)
}
bigfib (10000)
// "336447 ... 366875"
Expand the program below to run it in your browser
const Bignum =
{ fromInt: (n = 0) =>
n < 10
? [ n ]
: [ n % 10, ...Bignum.fromInt (n / 10 >> 0) ]
, fromString: (s = "0") =>
Array.from (s) .reverse ()
, toString: (b) =>
b .reverse () .join ("")
, add: (b1, b2) =>
{
const len = Math.max (b1.length, b2.length)
let answer = []
let carry = 0
for (let i = 0; i < len; i = i + 1) {
const x = b1[i] || 0
const y = b2[i] || 0
const sum = x + y + carry
answer.push (sum % 10)
carry = sum / 10 >> 0
}
if (carry > 0) answer.push (carry)
return answer
}
}
const { fromInt, toString, add } =
Bignum
const bigfib = (n = 0) =>
{
let a = fromInt (0)
let b = fromInt (1)
let _
while (n > 0) {
_ = a
a = b
b = add (b, _)
n = n - 1
}
return toString (a)
}
console.log (bigfib (10000))
Try not to use recursion for a simple problem like fibonacci. And if you'll only use it once, don't use an array to store all results. An array of 2 elements containing the 2 previous fibonacci numbers will be enough. In each step, you then only have to sum up those 2 numbers. How can you save 2 consecutive fibonacci numbers? Well, you know that when you have 2 consecutive integers one is even and one is odd. So you can use that property to know where to get/place a fibonacci number: for fib(i), if i is even (i%2 is 0) place it in the first element of the array (index 0), else (i%2 is then 1) place it in the second element(index 1). Why can you just place it there? Well when you're calculating fib(i), the value that is on the place fib(i) should go is fib(i-2) (because (i-2)%2 is the same as i%2). But you won't need fib(i-2) any more: fib(i+1) only needs fib(i-1)(that's still in the array) and fib(i)(that just got inserted in the array).
So you could replace the recursion calls with a for loop like this:
int fibonacci(int n){
if( n <= 0){
return 0;
}
int previous[] = {0, 1}; // start with fib(0) and fib(1)
for(int i = 2; i <= n; ++i){
// modulo can be implemented with bit operations(much faster): i % 2 = i & 1
previous[i&1] += previous[(i-1)&1]; //shorter way to say: previous[i&1] = previous[i&1] + previous[(i-1)&1]
}
//Result is in previous[n&1]
return previous[n&1];
}
Recursion is actually discommanded while programming because of the time(function calls) and ressources(stack) it consumes. So each time you use recursion, try to replace it with a loop and a stack with simple pop/push operations if needed to save the "current position" (in c++ one can use a vector). In the case of the fibonacci, the stack isn't even needed but if you are iterating over a tree datastructure for example you'll need a stack (depends on the implementation though). As I was looking for my solution, I saw #naomik provided a solution with the while loop. That one is fine too, but I prefer the array with the modulo operation (a bit shorter).
Now concerning the problem of the size long long int has, it can be solved by using external libraries that implement operations for big numbers (like the GMP library or Boost.multiprecision). But you could also create your own version of a BigInteger-like class from Java and implement the basic operations like the one I have. I've only implemented the addition in my example (try to implement the others they are quite similar).
The main idea is simple, a BigInt represents a big decimal number by cutting its little endian representation into pieces (I'll explain why little endian at the end). The length of those pieces depends on the base you choose. If you want to work with decimal representations, it will only work if your base is a power of 10: if you choose 10 as base each piece will represent one digit, if you choose 100 (= 10^2) as base each piece will represent two consecutive digits starting from the end(see little endian), if you choose 1000 as base (10^3) each piece will represent three consecutive digits, ... and so on. Let's say that you have base 100, 12765 will then be [65, 27, 1], 1789 will be [89, 17], 505 will be [5, 5] (= [05,5]), ... with base 1000: 12765 would be [765, 12], 1789 would be [789, 1], 505 would be [505]. It's not the most efficient, but it is the most intuitive (I think ...)
The addition is then a bit like the addition on paper we learned at school:
begin with the lowest piece of the BigInt
add it with the corresponding piece of the other one
the lowest piece of that sum(= the sum modulus the base) becomes the corresponding piece of the final result
the "bigger" pieces of that sum will be added ("carried") to the sum of the following pieces
go to step 2 with next piece
if no piece left, add the carry and the remaining bigger pieces of the other BigInt (if it has pieces left)
For example:
9542 + 1097855 = [42, 95] + [55, 78, 09, 1]
lowest piece = 42 and 55 --> 42 + 55 = 97 = [97]
---> lowest piece of result = 97 (no carry, carry = 0)
2nd piece = 95 and 78 --> (95+78) + 0 = 173 = [73, 1]
---> 2nd piece of final result = 73
---> remaining: [1] = 1 = carry (will be added to sum of following pieces)
no piece left in first `BigInt`!
--> add carry ( [1] ) and remaining pieces from second `BigInt`( [9, 1] ) to final result
--> first additional piece: 9 + 1 = 10 = [10] (no carry)
--> second additional piece: 1 + 0 = 1 = [1] (no carry)
==> 9542 + 1 097 855 = [42, 95] + [55, 78, 09, 1] = [97, 73, 10, 1] = 1 107 397
Here is a demo where I used the class above to calculate the fibonacci of 10000 (result is too big to copy here)
Good luck!
PS: Why little endian? For the ease of the implementation: it allows to use push_back when adding digits and iteration while implementing the operations will start from the first piece instead of the last piece in the array.

How does that recursive function work?

Might be a very basic question but I just got stuck with it. I am trying to run the following recursive function:
//If a is 0 then return b, if b is 0 then return a,
//otherwise return myRec(a/2, 2*b) + myRec(2*a, b/2)
but it just gets stuck in infinite loop. Can anybody help me to run that code and explain how exactly that function works? I built various recursive functions with no problems but this one just drilled a hole in my head.
Thanks.
Here is what I tried to do:
#include<iostream>
int myRec(int a, int b){
if (a==0){
return b;
}
if (b==0){
return a;
}
else return myRec(a/2, 2*b) + myRec(2*a, b/2);
}
int main()
{
if (46 == myRec(100, 100)) {
std::cout << "It works!";
}
}
Well, let us mentally trace it a bit:
Starting with a, b (a >= 2 and b >= 2)
myRec(a/2, 2*b) + something
something + myRec(2*a', b'/2)
Substituting for a/2 for a' and 2*b for b', we get myRec(2*(a/2), (b*2)/2), which is exactly where we started.
Therefore we will never get anywhere.
(Note that I have left out some rounding here, but you should easily see that with this kind of rounding you will only round down a to the nearest even number, at which point it will be forever alternating between that number and half that number)
I think you are missing on some case logic. I last program in C ages ago so correct my syntax if wrong. Assuming numbers less than 1 will be converted to zero automatically...
#include<iostream>
int myRec(int a, int b){
// Recurse only if both a and b are not zero
if (a!=0 && b!=0) {
return myRec(a/2, 2*b) + myRec(2*a, b/2);
}
// Otherwise check for any zero for a or b.
else {
if (a==0){
return b;
}
if (b==0){
return a;
}
}
}
UPDATE:
I have almost forgot how C works on return...
int myRec(int a, int b){
if (a==0){
return b;
}
if (b==0){
return a;
}
return myRec(a/2, 2*b) + myRec(2*a, b/2);
}
VBA equivalent with some changes for displaying variable states
Private Function myRec(a As Integer, b As Integer, s As String) As Integer
Debug.Print s & vbTab & a & vbTab & b
If a = 0 Then
myRec = b
End If
If b = 0 Then
myRec = a
End If
If a <> 0 And b <> 0 Then
myRec = myRec(a / 2, 2 * b, s & "L") + myRec(2 * a, b / 2, s & "R")
End If
End Function
Sub test()
Debug.Print myRec(100, 100, "T")
End Sub
Running the test in Excel gives this (a fraction of it as it overstacks Excel):
T: Top | L: Left branch in myRec | R: Right branch in myRec
The root cause will be the sum of the return which triggers more recursive calls.
Repeating of the original values of a and b on each branch from level 2 of the recursive tree...
So MyRec(2,2) = MyRec(1,4) + MyRec(4,1)
And MyRec(1,4) = MyRec(.5,8) + MyRec(2,2)
So MyRec(2,2) = MyRec(.5,8) + MyRec(2,2) + MyRec(4,1)
Oops.
(The .5's will actually be zeroes. But it doesn't matter. The point is that the function won't terminate for a large range of possible inputs.)
Expanding on gha.st's answer, consider the function's return value as a sum of expressions without having to worry about any code.
Firstly, we start with myRec(a,b). Let's just express that as (a,b) to make this easier to read.
As I go down each line, each expression is equivalent, disregarding the cases where a=0 or b=0.
(a,b) =
(a/2, 2b) + (2a, b/2) =
(a/4, 4b) + (a, b) + (a, b) + (4a, b/4)
Now, we see that at a non-terminating point in the expression, calculating (a,b) requires first calculating (a,b).
Recursion on a problem like this works because the arguments typically tend toward a 'base case' at which the recursion stops. A great example is sorting a list; you can recursively sort halves of the list until a list given as input has <= 2 elements, which is trivial without recursion. This is called mergesort.
However, your myRec function does not have a base case, since for non-zero a or b, the same arguments must be passed into the function at some point. That's like trying to sort a list, in which half of the list has as many elements as the entire list.
Try replacing the recursion call with:
return myRec(a/2, b/3) + myRec(a/3, b/2);

Fast inner product of ternary vectors

Consider two vectors, A and B, of size n, 7 <= n <= 23. Both A and B consists of -1s, 0s and 1s only.
I need a fast algorithm which computes the inner product of A and B.
So far I've thought of storing the signs and values in separate uint32_ts using the following encoding:
sign 0, value 0 → 0
sign 0, value 1 → 1
sign 1, value 1 → -1.
The C++ implementation I've thought of looks like the following:
struct ternary_vector {
uint32_t sign, value;
};
int inner_product(const ternary_vector & a, const ternary_vector & b) {
uint32_t psign = a.sign ^ b.sign;
uint32_t pvalue = a.value & b.value;
psign &= pvalue;
pvalue ^= psign;
return __builtin_popcount(pvalue) - __builtin_popcount(psign);
}
This works reasonably well, but I'm not sure whether it is possible to do it better. Any comment on the matter is highly appreciated.
I like having the 2 uint32_t, but I think your actual calculation is a bit wasteful
Just a few minor points:
I'm not sure about the reference (getting a and b by const &) - this adds a level of indirection compared to putting them on the stack. When the code is this small (a couple of clocks maybe) this is significant. Try passing by value and see what you get
__builtin_popcount can be, unfortunately, very inefficient. I've used it myself, but found that even a very basic implementation I wrote was far faster than this. However - this is dependent on the platform.
Basically, if the platform has a hardware popcount implementation, __builtin_popcount uses it. If not - it uses a very inefficient replacement.
The one serious problem here is the reuse of the psign and pvalue variables for the positive and negative vectors. You are doing neither your compiler nor yourself any favors by obfuscating your code in this way.
Would it be possible for you to encode your ternary state in a std::bitset<2> and define the product in terms of and? For example, if your ternary types are:
1 = P = (1, 1)
0 = Z = (0, 0)
-1 = M = (1, 0) or (0, 1)
I believe you could define their product as:
1 * 1 = 1 => P * P = P => (1, 1) & (1, 1) = (1, 1) = P
1 * 0 = 0 => P * Z = Z => (1, 1) & (0, 0) = (0, 0) = Z
1 * -1 = -1 => P * M = M => (1, 1) & (1, 0) = (1, 0) = M
Then the inner product could start by taking the and of the bits of the elements and... I am working on how to add them together.
Edit:
My foolish suggestion did not consider that (-1)(-1) = 1, which cannot be handled by the representation I proposed. Thanks to #user92382 for bringing this up.
Depending on your architecture, you may want to optimize away the temporary bit vectors -- e.g. if your code is going to be compiled to FPGA, or laid out to an ASIC, then a sequence of logical operations will be better in terms of speed/energy/area than storing and reading/writing to two big buffers.
In this case, you can do:
int inner_product(const ternary_vector & a, const ternary_vector & b) {
return __builtin_popcount( a.value & b.value & ~(a.sign ^ b.sign))
- __builtin_popcount( a.value & b.value & (a.sign ^ b.sign));
}
This will lay out very well -- the (a.value & b.value & ... ) can enable/disable an XOR gate, whose output splits into two signed accumulators, with the first pathway NOTed before accumulation.

Unexpected result regarding comparing two pointer-related integer values in C++

I have a BST of three elements {1, 2, 3}. Its structure looks like
2
/ \
1 3
Now I try to calculate the height for each node using BSTHeight() defined below and have some problem with calculating the height of '2', which value is supposed to be 1 as the heights of '1' and '3' are defined as 0. My problem is that with direct use of heights from '2's two children (see part 2 highlighted below), its height is ALWAYS 0. However, its value is correct if I use two temporary integer variables (see part 1 highlighted below). I couldn't see any difference between the two approaches in terms of functionality. Can anyone help explain why?
void BSTHeight(bst_node *p_node)
{
if (!p_node)
return;
if (!p_node->p_lchild && !p_node->p_rchild) {
p_node->height = 0;
} else if (p_node->p_lchild && p_node->p_rchild) {
BSTHeight(p_node->p_lchild);
BSTHeight(p_node->p_rchild);
#if 0 // part 1
int lchild_height = p_node->p_lchild->height;
int rchild_height = p_node->p_rchild->height;
p_node->height = 1 + ((lchild_height > rchild_height) ? lchild_height : rchild_height);
#else // part 2
p_node->height = 1 + ((p_node->p_lchild->height) > (p_node->p_rchild->height)) ? (p_node->p_lchild->height) : (p_node->p_rchild->height);
#endif
} else if (!p_node->p_lchild) {
BSTHeight(p_node->p_rchild);
p_node->height = 1 + p_node->p_rchild->height;
} else {
BSTHeight(p_node->p_lchild);
p_node->height = 1 + p_node->p_lchild->height;
}
}
Problem lies in operator precedence. Addition binds stronger than ternary operator, hence you must surround ternary operator (?:) with brackets.
Below is the corrected version. Note that all brackets you used were superflous and I've removed them. I've added the only needed pair instead:
1 + (p_node->p_lchild->height > p_node->p_rchild->height ?
p_node->p_lchild->height : p_node->p_rchild->height);
Even better would be to use std::max (from <algorithm>) instead:
1 + std::max(p_node->p_lchild->height, p_node->p_rchild->height)

Parsing version numbers to real numbers

I would like to determine if one version number is greater than another. The version number could be any of the following:
4
4.2
4.22.2
4.2.2.233
...as the version number is beyond my control, so I couldn't say how many dots could actually exist in the number.
Since the number is not really a real number, I can't simply say,
Is 4.7 > 4.2.2
How can I go about converting a number, such as 4.2.2 into a real number that could be checked against another version number?
I would preferably like a ColdFusion solution, but the basic concept would also be fine.
This is ripped from the plugin update code in Mango Blog, and updated a little bit. It should do exactly what you want. It returns 1 when argument 1 is greater, -1 when argument 2 is greater, and 0 when they are exact matches. (Note that 4.0.1 will be an exact match to 4.0.1.0)
It uses the CF list functions, instead of arrays, so you might see a small performance increase if you switched to arrays instead... but hey, it works!
function versionCompare( version1, version2 ){
var len1 = listLen(arguments.version1, '.');
var len2 = listLen(arguments.version2, '.');
var i = 0;
var piece1 = '';
var piece2 = '';
if (len1 gt len2){
arguments.version2 = arguments.version2 & repeatString('.0', len1-len2);
}else if (len2 gt len1){
arguments.version1 = arguments.version1 & repeatString('.0', len2-len1);
}
for (i=1; i lte listLen(arguments.version1, '.'); i=i+1){
piece1 = listGetAt(arguments.version1, i, '.');
piece2 = listGetAt(arguments.version2, i, '.');
if (piece1 neq piece2){
if (piece1 gt piece2){
return 1;
}else{
return -1;
}
}
}
//equal
return 0;
}
Running your example test:
<cfoutput>#versionCompare('4.7', '4.2.2')#</cfoutput>
prints:
1
If version 4 actually means 4.0.0, and version 4.2 actually means 4.2.0, you could easily convert the version to a simple integer.
suppose that every part of the version is between 0 and 99, then you could calculate an 'integer version' from X.Y.Z like this:
Version = X*100*100 + Y*100 + Z
If the ranges are bigger or smaller you could use factors higher or lower than 100.
Comparing the version then becomes easy.
Parse each number separately and compare them iteratively.
if (majorVersion > 4 &&
minorVersion > 2 &&
revision > 2)
{
// do something useful
}
// fail here
That's obviously not CF code, but you get the idea.
A version number is basically a period delimited array of numbers, so you can parse both versions into number arrays, and then compare each element in the first array to the corresponding element in the second array.
To get the array, do:
<cfset theArrayofNumbers = listToArray(yourVersionString, ".")>
and then you can do your comparisons.
You can split the string containing the version by periods, then start at the first index and compare down until one is greater than the other (or if they are equal, one contains a value the other does not).
I'm afraid I've never written in coldfusion but that would be the basic logic I'd follow.
This is a rough unoptimized example:
bool IsGreater(string one, string two)
{
int count;
string[] v1;
string[] v2;
v1 = one.Split(".");
v2 = two.Split(".");
count = (one.Length > two.Length) ? one.Length : two.Length;
for (int x=0;x<count;x++)
{
if (Convert.ToInt32(v1[x]) < Convert.ToInt32(v2[x]))
return false;
else if (Convert.ToInt32(v1[x]) > Convert.ToInt32(v2[x])
return true;
} // If they are the same it'll go to the next block.
// If you're here, they both were equal for the shortest version's digit count.
if (v1.Length > v2.Length)
return true; // The first one has additional subversions so it's greater.
}
There is no general way to convert multiple-part version numbers into real numbers, if there is no restriction on the size of each part (e.g. is 4.702.0 > 4.7.2?).
Normally you would define a custom comparison function by creating a sequence or array of version-number parts or components, so 4.7.2 is represented as [4, 7, 2] and 4.702.0 is [4, 702, 0]. Then you compare each element of the two arrays until they don't match:
left = [4, 7, 2]
right = [4, 702, 0]
# check index 0
# left[0] == 4, right[0] == 4
left[0] == right[0]
# equal so far
# check index 1
# left[1] == 7, right[1] == 702
left[1] < right[1]
# so left < right
I don't know about ColdFusion, but in some languages you can do a direct comparison with arrays or sequences. For example, in Python:
>>> left = [4, 7, 2]
>>> right = [4, 702, 0]
>>> left < right
True