2

In my little project here I have sorted a list in decending order, however, my goal is to sort it in this custom pattern. (largest -> smallest -> next largest -> next smallest ->)etc.

In java I was able to do this like this:

public static void wackySort(int[] nums) {
    //first, this simply sorts the array by ascending order.
    int sign = 0;
    int temp = 0;
    int temp2 = 0;
    for (int i = 0; i < nums.length; i++) {
        for (int j = 0; j < nums.length -1; j++){
            if (nums[j] > nums[j+1]) {
               temp = nums[j];
               nums[j] = nums[j+1];
               nums[j+1] = temp;
            }
        }
    }

    //prepare for new array to actually do the wacky sort.
    System.out.println();
    int firstPointer = 0;
    int secondPointer = nums.length -1;
    int[] newarray = new int[nums.length];
    int size = nums.length;

    //increment by two taking second slot replacing the last (n-1) term
    for (int i = 0; i < nums.length -1; i+=2) {
        newarray[i] = nums[firstPointer++];
        newarray[i+1] = nums[secondPointer--];
    }

    //store those values back in the nums array    
    for (int i = 0; i < nums.length; i++) {
        nums[i] = newarray[i];
    }
}

My goal is to do the same thing but in python, except backwards. Any ideas on how to convert that last for loop that does the wackysort into python and make it go backwards?

Elazar
  • 20,415
  • 4
  • 46
  • 67
Binka
  • 111
  • 4
  • 11
  • So you basically want someone to port your code to python? Why don't you do it yourself? – enpenax Jul 02 '13 at 22:32
  • I have it all done in python already, I just don't know how to convert that last for loop to python and make it go backwards :) – Binka Jul 02 '13 at 22:33
  • 1
    Trying to convert code from Java to Python is almost always a bad idea. You end up with bad code that's hard to read and maintain, runs slow, and has bugs you didn't expect (because the Java version doesn't). – abarnert Jul 02 '13 at 22:33
  • What do you mean by "make it go backwards?" Do you mean you want the result of your java program, but inverted? – 2rs2ts Jul 02 '13 at 22:38
  • Kind of, my java program goes by this pattern (smallest -> largest -> next smallest -> next largest -> ...). I'm trying to make my python program do (largest -> smallest -> next largest -> next smallest ->) – Binka Jul 02 '13 at 22:41
  • @Binka Ok, please be more explicit next time, it helps a lot - when reading your code we will wonder what the heck you are doing :P – 2rs2ts Jul 02 '13 at 22:42
  • 1
    @Binka: In particular, giving some sample input and the expected output really helps. That way, if we're not sure we understand your description (or, worse, if we _are_ sure, but we're wrong—which happens a lot, at least to me…), we can check it against your sample and see. – abarnert Jul 02 '13 at 23:27

5 Answers5

6
nums = [1, 2, 3, 4]
newarray = sum(zip(reversed(nums), nums), ())[:len(nums)]

>>> print(newarray)
(4, 1, 3, 2)

What it does, step by step. first, reversed():

>>> list(reversed(nums))
[4, 3, 2, 1]

Then zip():

>>> list(zip([4, 3, 2, 1], [1, 2, 3, 4]))
[(4, 1), (3, 2), (2, 3), (1, 4)]

You can see we have almost the list we want, we have a problem: these are tuples. we want to flatten them.

>>> (4, 1) + (3, 2) + (2, 3) + (1, 4)
(4, 1, 3, 2, 2, 3, 1, 4)

Oh. That's nice. But how to do it inside the list? Simple: use sum(), which does exactly this - adding many things together. Only we need to give it something to start with - an empty tuple ():

>>> sum([(4, 1), (3, 2), (2, 3), (1, 4)], ())
(4, 1, 3, 2, 2, 3, 1, 4)

But the we don't want the second half, so let's remove it. We know he list is exactly twice too long, yes?

>>> (4, 1, 3, 2, 2, 3, 1, 4)[:len(nums)]
(4, 1, 3, 2)

That's it.


Another option:

from itertools import chain, islice
a = list(islice(chain.from_iterable(zip(nums, reversed(nums))), len(nums)))
Elazar
  • 20,415
  • 4
  • 46
  • 67
  • I'm pretty sure he wants `[4, 1, 3, 2]`. So you're doing it backward, and doing twice as much as you should be. – abarnert Jul 02 '13 at 22:46
  • Of course you can just toss on a `reversed` and cut it at the mid-point (rounding up). The wasted work is harmless, because it's just O(N) work after an O(N^2) sort… – abarnert Jul 02 '13 at 22:47
  • Thanks, this is close, however my goal is to have the pattern (largest -> smallest -> next largest -> next smallest ->). It should read (4,1,3,2) – Binka Jul 02 '13 at 22:47
  • @2rs2ts: Why make it more complicated? The way he's doing it now (basically what I suggested, except reversing before instead of after, and using the original length so you don't need to find the midpoint) is a lot simpler. – abarnert Jul 02 '13 at 22:58
  • 2
    @Elazar: No, the new version only works for even-length lists. Go back to the previous one, `sum(zip(nums, reversed(nums)), ())[:len(nums)]`. It's dead simple and obvious, and again, who cares about the "wasted work" of copying an extra half list one time? – abarnert Jul 02 '13 at 22:59
  • @abarnert I guess you are right. I don't really know what is better. wasted work is frowned upon around here. personally I don't care. – Elazar Jul 02 '13 at 23:00
  • @abarnert I'm trying to understand your code, but I'm supposed to have this all in one method and then return the final list. Any way your way can do this? – Binka Jul 02 '13 at 23:01
  • 2
    @Elazar: Anyone who downvotes you for not wasting time optimizing out an extra N/2 steps in an O(N^2) algorithm isn't worth listening to. The time you spend getting it right, you could answer dozens of other questions here, or even get some real work/homework/hobby/whatever done, right? And then, everyone else has to waste time understanding the more complicated version once you're done. – abarnert Jul 02 '13 at 23:03
  • @Binka: If you're asking about my answer, you should post it as a comment to my answer. If you're asking about Elazar's answer, please be clearer about who you're talking to. – abarnert Jul 02 '13 at 23:04
  • Okay sorry this is so confusing, but this wackysort method is being called by another file, and it has to be this way. I tried this, but it only return the sorted array, it never returned the custom pattern http://pastebin.com/YnErfzuk – Binka Jul 02 '13 at 23:08
  • @abarnert I felt compelled to make the zip shorter, but it doesn't work for odd-length lists as you said. Can't have my cake and eat it too... – 2rs2ts Jul 02 '13 at 23:13
  • @Binka No need to have `newlist = [len(vals)]` in there, but even if you left that in there, that code works. Check again to see if you're making a mistake in your input or output... try `print rws.reverseWackySort([1,3,4,2])`. Or if Python 3, `print (rws.reverseWackySort([1,3,4,2]))`. – 2rs2ts Jul 02 '13 at 23:24
  • @Elazar: This already _is_ O(N). `reversed` is O(N), so is `zip`, so is `sum`, and so is cutting off the last half of the list. If you're very clever, you can get it down to O(N/2), but O(N/2) == O(N), so why bother? Especially since it's being run after an O(N^2) sort algorithm? – abarnert Jul 02 '13 at 23:25
  • @abarnert are you *sure* `sum` is O(N) ? it sums tuples, after all. and not 2-tuples. – Elazar Jul 02 '13 at 23:30
  • @Elazar: You've got N/2 calls to `tuple.__add__`. Each time, it's called on longer and longer `self`, but its argument is always a 2-tuple. Ignoring memory reallocation and copying (which is amortized constant time because of exponential growth), the only cost is copying 2 elements each time, N/2 times. – abarnert Jul 02 '13 at 23:36
  • @abarnert I think you are right - I've just tested it and it seems pretty much linear. – Elazar Jul 02 '13 at 23:39
  • 1
    @abarnert Running the first solution through timeit with `nums=[1,2,3,4]` took 1.72s, while the second with the same took 2.83s. But, with `nums=list(range(1000))` the second took 84.8s and the first has been running for 4 minutes now and isn't done. At this point we're just nitpicking over optimizations, while OP doesn't understand what we've been writing haha. – 2rs2ts Jul 02 '13 at 23:40
  • Sorry, was AFK for a little bit. Is it decided that newarray = sum(zip(reversed(nums), nums), ())[:len(nums)] works? – Binka Jul 02 '13 at 23:42
  • @Binka yes. Elazar, put your `itertools` solution back up! It may not be good for OP's purposes but it sure is a good reference, and much more efficient with large data sets.... – 2rs2ts Jul 02 '13 at 23:42
  • 1
    @Elazar Great! By the way, in case you were wondering, yes, it's still running. `top` thinks it's using 100.6% of my CPU, amusingly. – 2rs2ts Jul 02 '13 at 23:47
  • H'mm, I'll show you what I have. For some reason it isnt returning the newlist properly. Sorry about the classes and files, my teacher says it has to be this way :p It still just returns it sorted normally. http://pastebin.com/wqLLScjH – Binka Jul 02 '13 at 23:49
  • @Binka you mean to write `nums = rws.reverseWackySort(nums)`. It doesn't modify `nums` in place, you have to assign the returned result to something. Might was well reassign it to the original thing. – 2rs2ts Jul 02 '13 at 23:50
  • Ahhh, I didn't realize you had to assign it like that. Thank you very much. – Binka Jul 02 '13 at 23:51
  • @2rs2ts are you sure it is a `list(range(1000))`? not 100000 or something? – Elazar Jul 02 '13 at 23:53
  • Is there anyway you could explain 'newlist = sum(zip(reversed(vals), vals), ())[:len(vals)]` does exactly? It's very confusing – Binka Jul 02 '13 at 23:53
  • @Elazar I'm pretty sure I have this correct: `timeit.timeit(stmt='sum(zip(reversed(nums),nums),())[:len(nums)]',setup='nums=list(range(1000))')` – 2rs2ts Jul 02 '13 at 23:55
  • @Binka Read these: [`zip()`](http://docs.python.org/2/library/functions.html#zip), [`sum()`](http://docs.python.org/2/library/functions.html#sum), [`reversed()`](http://docs.python.org/2/library/functions.html#reversed), and [`lists`](http://docs.python.org/2/tutorial/introduction.html#lists) for some slicing examples and [this question](http://stackoverflow.com/questions/509211/the-python-slice-notation) for more. And try typing some of these things in the interpreter to see what you get! "Tell me and I forget, show me and I remember." Although I recall something about horses and water. – 2rs2ts Jul 03 '13 at 00:02
  • @2rs2ts I think `setup` is what eats your CPU. – Elazar Jul 03 '13 at 00:06
  • @Elazar I doubt it, here's the other one: `timeit.timeit(stmt='list(islice(chain.from_iterable(zip(nums,reversed(nums))),len(nums)))',setup='from itertools import chain, islice; nums=list(range(1000))')`. Anyway it finally finished, with a grand total of 2309 seconds. Yep! – 2rs2ts Jul 03 '13 at 00:14
  • @2rs2ts try to do the `setup` simply by assigning to a global variable. you don't change it anyway. – Elazar Jul 03 '13 at 00:19
  • @Elazar The setup code only gets run once, according to [this answer](http://stackoverflow.com/a/8220943/691859). – 2rs2ts Jul 03 '13 at 00:33
  • Also, even if setup _did_ run 1000000 times, it would only take a few microseconds per loop, so that wouldn't be enough to drag on for an hour. – abarnert Jul 03 '13 at 00:58
  • but if I'm doing it with a global variable it takes less than a couple of seconds for n < 100000. – Elazar Jul 03 '13 at 01:00
  • 1
    @2rs2ts: However, it is usually worth trying to write the statement as an expression rather than a string (ideally just a call to a local function), letting your local/global environment be the setup. It makes everything a lot simpler, and also has less potential for people objecting "but the setup takes too long…" – abarnert Jul 03 '13 at 01:00
  • @2rs2ts: One more thing: If you use iPython, the magic `%timeit` tries to do however many loops fit into a couple seconds, instead of a fixed number of loops. So, when something turns out to be 150x slower than you expected, instead of waiting for 2 minutes and canceling and saying, "Well, it's somewhere between 120x and infx slower", you find out within 2 seconds that it's around 150x slower. – abarnert Jul 03 '13 at 01:04
  • @abarnert Do you mean to do something like `timeit.timeit(stmt="foo()",setup="from __main__ import foo")`? I've only just begun fiddling with this module, so I welcome advice! – 2rs2ts Jul 03 '13 at 01:05
  • 1
    @2rs2ts: No, just `timeit.timeit(stmt=foo)`. That does exactly the same thing as what you just tried to do. But again, ipython makes it much easier: just type `%timeit foo()` at the interpreter prompt. – abarnert Jul 03 '13 at 01:10
  • If you want your stuff to stick around, put it in an answer -- comments are hard to follow, and comments with code are especially hard to read (especially in a whitespace specific language like Python) – George Stocker Jul 03 '13 at 12:09
5

I would suggest sorting it normally first, then doing your shuffle:

inlist=[3,5,7,6,9,8,2,1]
inlist.sort()
outlist=[]
while len(inlist)>0:
  if (len(outlist)%2==0):
      outlist.append(inlist.pop())
  else:
      outlist.append(inlist.pop(0))
AMADANON Inc.
  • 5,753
  • 21
  • 31
  • interesting. What does the .pop do? – Binka Jul 02 '13 at 22:39
  • it removes and returns the last value from the list – John Jul 02 '13 at 22:40
  • [Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list.](http://docs.python.org/2/tutorial/datastructures.html#more-on-lists) – 2rs2ts Jul 02 '13 at 22:41
  • @Binka: `help(inlist.pop)` will answer such questions and should be your first port of call: "L.pop([index]) -> item -- remove and return item at index (default last). Raises IndexError if list is empty or index is out of range." – Chris Morgan Jul 02 '13 at 22:49
  • Did this work for you, because for me it printed out an empty string list with this test list. nums = [88, 1, 7, 32, 18, 77, 34, 99, 54, 22] – Binka Jul 02 '13 at 22:50
  • I cut & paste the above code directly into python, and at the end, outlist contained: [9, 1, 8, 2, 7, 3, 6, 5] – AMADANON Inc. Jul 02 '13 at 22:52
  • 1
    Why `while len(inlist)>0:`? That's guaranteed to be the same thing as `while inlist:`, but harder to read, slower, and non-PEP-8-compliant. – abarnert Jul 02 '13 at 23:00
  • @Binka: Your new code is not the same as the code you copied (e.g., where did that `new list = [len(vals)]` come from?), so why do you expect it to do the same thing? – abarnert Jul 02 '13 at 23:01
  • I'm just declaring a new list, do I not need to? – Binka Jul 02 '13 at 23:02
  • 1
    First, you never need to "declare" anything in Python. Second, `[len(vals)]` is a new list with a single item, whose value is the length of `vals`. – abarnert Jul 02 '13 at 23:05
  • Ok, so first of all, `outlist[]` should start empty. Second of all, don't sort the list yourself, just call `list.sort()` - any `sort` of yours or mine can't hope to beat the built-in one (except in very specific cases). Assuming that code is still part of the class (if not, remove `self` from the function parameter list), it works fine for me. – AMADANON Inc. Jul 02 '13 at 23:06
  • 1
    @AMADANONInc.: The reason he's sorting himself is that it was a homework assignment to do so. (That's from a previous question.) And apparently the useless class design is also the professor's fault (which implies that we can't blame Binka too much; he's doing pretty well given that he's trying to learn from an idiot…). – abarnert Jul 02 '13 at 23:07
  • abarnert: re why `while len(inlist)...` - fair comment, too many languages in my head. – AMADANON Inc. Jul 02 '13 at 23:08
0

That last for loop:

for (int i = 0; i < nums.length; i++){
    nums[i] = newarray[i];

… is a one-liner in Python. The exact equivalent is:

nums[:] = newarray[:len(nums)]

However, most likely, all you really need is:

nums = newarray

If you really want to write it in "Java style" for some reason, it would be this:

i = 0
while i < len(nums):
    nums[i] = newarray[i]
    i += 1

But that does exactly the same thing as the first version, except a slower more slowly and less readably.


Meanwhile, for the previous loop:

for (int i = 0; i < nums.length -1; i+=2){
    newarray[i] = nums[firstPointer++];
                newarray[i+1] = nums[secondPointer--];

Again, you can translate this more-or-less directly to Python:

i = 0
while i < len(nums)-1:
    newarray[i] = nums[firstPointer]
    firstPointer += 1
    newarray[i+1] = nums[secondPointer]
    secondPointer -= 1
    i += 2

But as with the last version of the last loop, this is horribly unreadable, and you'll be a lot happier if you try to describe the algorithm, then write that in Python.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • My mistake, I wasn't clear. It's the for loops above that, that actually does the wackysort. I'm not sure how to convert those ++'s and --'s in python – Binka Jul 02 '13 at 22:37
  • Do you see the `i += 1` in my answer? That's the closest thing Python has to `i++`. – abarnert Jul 02 '13 at 22:38
  • @Elazar: But the "make it go backwards" doesn't happen in this part of the code. Also, I think I know what he actually wants, from a previous question. But this answer tells him how to do what he asked for, which I think is worth writing. – abarnert Jul 02 '13 at 22:39
  • I would like to point out that the best way for OP to "allocate" a new array would be to do `[None] * len(nums)`, although I wouldn't use the subscripting there, I'd just `append()`. – 2rs2ts Jul 02 '13 at 22:57
0

As I said in a comment on your previous question, the simplest way to do what (I think) you want is to first write a function that can swap an already-sorted list into your desired order, then you can just chain your sorting function and the new function together.

def wacky_sort(seq):
    # code you already have

def alternate_ends(seq):
    new_seq = []
    while seq:
        new_seq.append(seq.pop(0))
        if seq:
            new_seq.append(seq.pop())
    return new_seq

def wacky_sort_with_alternating_ends(seq):
    wacky_sort(seq)
    return alternate_ends(seq)
abarnert
  • 354,177
  • 51
  • 601
  • 671
0

If you want to slove this question in O(1)extra space and O(n)time then you can solve like below code.

def rearrange(arr, n):

k,j=1,1
a=[0]*n
for i in range(0,n):

    if i%2==0:
        a[i]=arr[n-k]
        k=k+1

    else:
        a[i]=arr[i-j]
        j=j+1
for i in range(n):
    arr[i]=a[i]