Time Complexity: Nested 'if-then-else' - if-statement

I've written a recursive program in Scheme, and I'm having trouble getting the time complexity of it. I believe it goes out to be O(log(n)), but I am definitely no expert on this time complexity. Can you help me try and work out the complexity of this?
Here's my pseudo-code:
function A {
for (int i = 0; i < length.list(); i++)
{
if (list is null)
output absolute value of result
if (s2 < s1)
recall function A, adding item to s2 value.
else
recall function A, adding item to s1 value.
}
}
Here's the actual code in Scheme:
(define min-split (lambda (L s1 s2 mini)
(cond
((null? L)
(if (> 0 mini)
(min-split L s1 s2 (- mini (+ mini mini)))
mini
)
mini
)
((> s2 s1)
(min-split (cdr L) (+ s1 (car L)) s2 (- (+ (car L) s1) s2))
)
(else
(min-split (cdr L) s1 (+ s2 (car L)) (- (+ (car L) s2) s1))
)
)
)
)
Thanks!

If-then-else statements are of constant order, O(1).
It doesn't matter how many nested if-statements you have, because you will have coded a finite amount, which is always constant order.
I would need to see more code involving your recursive call to see what the time complexity of the algorithm is, but the if-statement is only useful in creating a base case. It doesn't in itself contribute to the complexity of your program in any meaningful way.
Take for example the following program:
int foo(List<Integer> bar) {
if (bar.size() == 0)
return -1;
if (bar.size() > 0) {
bar.remove();
return foo(bar);
}
return bar.remove();
}
The runtime complexity of this recursive program is O(n) because, upon analyzing it, we can see that a recursive call is being made for each item in our List, bar.
So, the worst-case is O(n), because foo will be called at most the number of times, n, as the size of the list. The best-case is O(1). The if statements have no bearing on the complexity in themselves. They only supply the base / recursive case.

Related

Why is the performance of reverse O(n^2) in Scheme?

Let's say my reverse function in Scheme is coded in the following way:
(define (reversee lst)
(if (null? lst)
'()
(append (reversee (cdr lst)) (list (car lst)))))
Why is the performance of this procedure O(n^2)?
Well, consider what the function has to do:
for a zero-element list it just returns the list.
for a 1 element list it appends the reverse of a zero element list and the first element.
for a 2 element list it appends the reverse of a 1 element list and the first element
... and so on.
It's obvious from this that to reverse an n-element list it calls itself recursively n times. So this looks like the time complexity is O(n), right?
Not so fast: for each recursive call it appends two lists. So we need to worry about the complexity of append. Well, let's write the best append we can:
(define (my-append l1 l2)
(if (null? l1)
l2
(cons (car l1) (my-append (cdr l1) l2))))
OK, so what's the complexity of this? First of all, only depends on the length of l1, so we only need to worry about that:
if l1 is a zero-element list it returns l2
if l2 is a 1-element list is conses its first element onto the result of appending the rest of it to l2
... and so on
So to append an n-element list to some other list there are n steps. So what is the time complexity of each of these steps? Well, it's constant: cons and cdr and car take constant time. So the time complexity of append is the length of the first list.
So this means that the time complexity of your reverse function is
n + n-1 + n-2 + ... + 1
It's a famous result that the value of such a sum is n(n+1)/2 (you do get this result by writing it as ((n + 1) + (n - 1 + 2) + ... (1 + n))/2 = ((n + 1) + ... (n + 1))/2 with n terms in the sum. So the complexity is n(n+1), which is asymptotically equal to n^2.
There is a better version of reverse, of course.
A more readable and understandable O(N) reverse....
(define (reverse lst)
(let reverser ((lst lst)
(reversed '()))
(if (null? lst)
reversed
(reverser (cdr lst) (cons (car lst) reversed)))))
(reverse '(1 2 3))
By building up the reversed list as it goes with a tail-recursive approach, it only has to traverse the list once.

Creating a list of n elements in Scheme/lisp?

I am trying to create a list of n elements. It must produce this output:
(my-list 5)
>> 0 1 2 3 4
I have the function below:
(define (my-list n)
(cond
((<= n 0) '())
(else (reverse-list (cons (- n 1)
(my-list(- n 1)))))
)
)
and this is producing
(my-list 10)
>>(8 6 4 2 0 1 3 5 7 9)
I understand this is due to reversing the list at every recursive call, but I am not sure what is the correct way. Also my reverse-list is working fine.
Thanks in advance!
A standard idiom in Scheme 'build-and-reverse' suggests you only reverse the list once, at the very end, when its reverse has been completely built (thus reducing the complexity down to O(N) from quadratic.)
So yes, you end up in a tail call to reverse but the list should be built without doing it. Scheme has plenty of local recursive binding constructs.
But.
If you build a range starting with the largest value (that should be one greater than the last element to the list) you don't need to reverse it in the end, at each iteration step you decrease a counter and prepend its new value to those already accumulated:
(define (range n)
(let rng ((m (- n 1)) (ret-val '())) ; named-let is very useful for small local recursive closures
(if (< m 0) ; that original (<= n 0) check is also handled here
ret-val ; here, the result is returned; note we don't need to reverse it
(rng (- m 1) (cons m ret-val))))))
(display (range 10))
(newline)
prints
(0 1 2 3 4 5 6 7 8 9)
Or, to demonstrate the build-and-reverse, we can start with the lowest value:
(define (range-asc n)
(let rng ((m 0) (ret-val '()))
(if (= m n)
(reverse ret-val) ; since we started from zero, we need to reverse it
(rng (+ m 1) (cons m ret-val)))))
(Looks like I still remember/can recover some Scheme. :-O)
First of all, your code. Properly formatted it should look something like:
(define (my-list n)
(cond ((<= n 0) '())
(else (reverse-list (cons (- n 1)
(my-list (- n 1)))))))
Problem you have, is reverse-list call. It happens every time you add new element to the list. You can fix it in many ways, but simple solution is to wrap your recursive code into local function, and do some additional operations (reverse in your case) when it returns.
(define (my-list n)
(define (build-list m)
(cond ((<= m 0) '())
(else (cons (- m 1)
(build-list (- m 1))))))
(reverse-list (build-list n)))
So, inside my-list function, first we define recursive part as a local function build-list. This is exactly your code, but with call to reverse-list removed. This part will build your list, but as you know, in reverse order. But that is no longer a problem, since we can reverse it when local function returns.

List of prime numbers from 2 to n (scheme)

I am trying to create a procedure which builds a list of prime numbers from 2 to any number n. Of course, this needs to be done iteratively, but I do not exactly know what I'm doing wrong.
(define (list-2-to-n n)
(define (iter n result)
(if (= n 0)
result
(if (< n 2)
'()
(if (= (remainder n 3) 0)
(list-2-to-n (- n 1))
(if (even? n)
(list-2-to-n (- n 1))
(iter (- n 1) (cons n result)))))))
(iter n '()))
Whenever test cases are passed through the procedure, it always returns ()
what I'm doing wrong.
Let's see. First of all, your indentation is woefully misleading. It really should correctly reflect the code's structure:
(define (list-2-to-n-0 n) ; a global function
(define (iter n result) ; an internal function
(if (= n 0)
result
(if (< n 2) ; **else**
'()
(if (= (remainder n 3) 0)
(list-2-to-n (- n 1))
(if (even? n) ; **else**
(list-2-to-n (- n 1))
(iter (- n 1) (cons n result)))))))
(iter n '()))
If not, at least you should've clearly marked the alternative clauses with some comments.
Now, you test whether the input number is divisible by 2 or 3, and respond in exactly the same manner. The two cases should really be joined then into one. Also, we can use cond here instead of so many nested ifs:
(define (list-2-to-n-1 n) ; a global function
(define (iter n result) ; an internal function
(cond
((= n 0) result)
((< n 2) '())
((or (= (remainder n 2) 0) (= (remainder n 3) 0))
(list-2-to-n (- n 1)) ; calling a global function, and
(else ; returning its result
(iter (- n 1) ; calling internal function, and
(cons n result))))) ; returning its result
(iter n '()))
Calling the global function there means starting the whole process anew - it will call its iter with the initial accumulator argument of (). Your supposedly result-returning case of n==0 is actually unreachable, for any initial value of n above 0, because the case n < 2 will be encountered first, and () will be returned (exactly the observed behaviour).
You shouldn't start anew i.e. you should always call the internal function. You should fix your base case. Last but not least, checking for divisibility by 2 or 3 only won't be enough, already for 5. So let's write isPrime there, and implement it later on:
(define (list-2-to-n-1 n) ; a global function
(define (iter n result) ; an internal function
(cond
((< n 2) result)
((not (isPrime n))
(iter (- n 1) result)) ; calling internal function, without
(else ; altering the accumulator
(iter (- n 1) ; calling internal function,
(cons n result))))) ; altering the accumulator
(iter n '()))
isPrime needs to try dividing n by 2,3,... Doesn't need to try dividing by any evens above 2, just odds will be enough. Doesn't need to try out any potential divisor d such that d * d > n, because if n = f * d, f <= d then f*d <= d*d i.e. n <= d*d.
Of course trying dividing by any composite like 9, 15, 77, is superfluous too - if any of those divide n then one of their prime factors 3, 5, 7, 11 divide it too, and we would detect them earlier. So actually, trying dividing by primes only is enough. To be able to do that, you need to restructure your code so that it builds its resulting list of primes in ascending order, and uses its prefix portion not greater than sqrt(k) to test each candidate k.
This is known as trial division algorithm. Then there's a much faster sieve of Eratosthenes.

Lisp S-Expressions and Lists Length/Size

I am trying to write a function using Common Lisp functions only that will count how many s-expressions are in an s-expression. For example:
((x = y)(z = 1)) ;; returns 2
and
((x - y)) ;; returns 1
Nested expressions are possible so:
((if x then (x = y)(z = w))) ;; returns 3
I wrote a function which finds the length and it works if no nested expressions are there. It is:
(define (length exp)
(cond
((null? exp) 0)
(#t (+ 1 (length (cdr exp))))))
Now I modified this in an attempt to support nested expressions to the following:
(define (length exp)
(cond
((null? exp) 0)
((list? (car exp)) (+ 1 (length (cdr exp))))
(#t (length (cdr exp)))))
This works for expressions with no nests, but is always 1 less than the answer for nested expressions. This is because taking the example above, ((if x then (x = y)(z = w))), this will look at if at first and which satisfies the third condition, returning the cdr (the rest of the expression as a list) into length. The same happens up until (x=y) is reached, at which point a +1 is returned. This means that the expression (if x then .... ) has not been counted.
In what ways would I be able to account for it? Adding +2 will over-count un-nested expressions.
I will need this to work in one function as nesting can happen anywhere, so:
((x = y) (if y then (z = w)))
At first sight, your code only recurses to the right (cdr-side) and not to the left (car-side), so that definitely is a problem there.
On second sight, this is even a little bit more tricky than that, because you are not exactly counting conses; you need to differentiate the case where a cons starts a proper list vs where it's the cdr of a list. If you were to recurse to the car and cdr, that information would be lost. We need to iterate over the sexp as a proper list,
(defun count-proper-list (sexp)
(cond ((atom sexp) 0)
(t (1+ (reduce #'+ (mapcar #'count-proper-list sexp))))))
But this will also count the top level list, therefor always return one more than what you seem to want. So perhaps,
(defun count-proper-sublist (sexp)
(1- (count-proper-list sexp)))

How to create and add elements in a list in Scheme?

I want to define a method that take an integer as input and creates dynamically a list of all descending integer numbers to zero. I find trouble into calling method for the n-1 element
It's not all that pretty but this should work, tested in DrScheme.
(define (gen-list x )
(if (= x 0) (list 0) (cons x (gen-list (- x 1)))))
Now that it's a homework question, I think a tail-recursive version could be an alternative.
(define (gen-list x)
(let lp ((n 0) (ret '()))
(if (> n x)
ret
(lp (1+ n) (cons n ret)))))
If you're using PLT scheme, the comprehensions library will let you do this rather neatly:
; natural -> (listof natural)
(define (list-to-zero start-num)
(for/list ([i (in-range start-num 0 -1)])
i))
Just an alternative to the recursive form...