Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

toggle shuffle mode #30

Closed
SpaceCadet2000 opened this issue Nov 5, 2014 · 8 comments
Closed

toggle shuffle mode #30

SpaceCadet2000 opened this issue Nov 5, 2014 · 8 comments
Assignees

Comments

@SpaceCadet2000
Copy link

I was wondering if it would be possible to add a hotkey that allows you to toggle the shuffle mode on and off while you are viewing images.

The way I imagine it would work is as follows:

  • User starts pqiv with the --shuffle option to flip randomly through a large collection of images
  • User sees something he likes, and wants to see the next or previous image in the set rather than the next random image
  • User presses the hotkey "R" (for random) to toggle shuffle mode off, so that he can view the images that are alphabetically adjacent to the one he was viewing.
@phillipberndt
Copy link
Owner

That's a neat idea, but not trivial to implement due to the way --shuffle works: pqiv stores its file list in a binary search tree. Normally, files are indexed by an increasing integer sequence. In sort mode, the file names are used as indices. In shuffle mode, random integegers are used. This obviously doesn't readily allow to toggle between random and sequential image movement at runtime, because the tree would have to be completely regenerated, which is way too expensive.

pqiv woks this way to allow the program to insert new and remove deleted images at runtime without reshuffling all images and to allow the user to navigate forward and backward through the shuffled images without changing the order (thus to be able to navigate away from an image and then reliably back to it). I'd want any alternative solution to retain that.

Some preminilary ideas:

  • We could store a permutation of the image ranks (= sequential index within the search tree) as a doubly linked list and use that for relative image movements.
  • We could use a LCG or some other prng whose state only depends on the last random number and which generates full cycles for relative image movements

I'll have to think about a good solution for this. Suggestions are very welcome.. ;-)

@phillipberndt phillipberndt self-assigned this Nov 6, 2014
@SpaceCadet2000
Copy link
Author

Ah I imagined that it would not be as trivial as it sounds :) Programming is not my trade, so I'm afraid I can't really be helpful with the implementation.

I do remember that older versions of the original qiv supported this behavior, up until version 1.6 I believe. Perhaps you could find inspiration in the source code? As a matter of fact, I kept an old qiv binary around for a long time because of this. It stopped working on recent distributions, because the libraries it depends on are no longer supported, hence the reason why I'm looking for a replacement :)

@phillipberndt
Copy link
Owner

As a quick progress update, here's some further thoughts on this. Don't expect anything too soon, this problem is even more complex than I imagined. The first part of this comment is a question, the second one is rather technical and probably not of interest to you.

How should the mechanism work?

For one, I thought about how the toggle option should work. If you initially had shuffle mode enabled, and then disabled it and looked at some images in sequential order, and then reenable it - what should be the next images displayed to the user? Should that choice be made on the basis of the last image that was displayed before shuffle was disabled, or the one that is currently displayed? Here's an example: Let's say you have 10 images. pqiv generates a random order, say

3 5 6 4 2 1 8 7 9 10

You start looking at the images until image 6 and then switch to sequential order until image 8, then reenable shuffle mode. Which images should be displayed now, in which order? Reasonable answers include

3 5 6 (switch) 7 8 (switch) 7 9 10 3 5 6 ..    (i.e. continue from 8)
3 5 6 (switch) 7 8 (switch) 4 2 1 8 7 9 10 ..  (i.e. continue from 6)
3 5 6 (switch) 7 8 (switch) 4 2 1 9 10 3 ..    (i.e. continue from 6, but skip those that the user already saw) 
3 5 6 (switch) 7 8 (switch) 4 2 1 9 10 xx ..   (i.e. continue from 6, but skip those that the user already saw, and generate a new random permutation when all images have been seen) 

The last option feels best to me, because it doesn't display any image twice before all the images have been shown, but it is also by far the hardest to implement. What do you think?

Implementation

I think that the best solution is to use a linear congruential generator as suggested above. The benefit over better generators is that we can easily invert the formula using the extended euclidean algorithm, thus move backwards through the images without having to store the actual sequence of images. We should use the actual number of images as the modulus (at least limit it to some constant times that number, as there are some O(m) operations involved in moving to the next image) and generate the generator's parameters on the fly from rand() (of course under the well-known constraints for full-cycles) to ensure that the order changes. Images that have been visited should be marked as "seen" and random forward movement through the images should be limited to images that have not. Once all images have been "seen", the meaning of the flag is inverted and a new sequence is generated.

With this solution,

  • the memory overhead is minimal (one flag as opposed to multiple lists or a tree)
  • the amount of new code required is minimal
  • random backward image movement doesn't neccessarily show the image that a user viewed before the current one, because the previous image in random order might be one that has already been seen in sequential order
  • there is no "random index" to keep track of - the problem of finding the next/previous random order image from the current sequential index is trivial, because the current image's index is the seed for the random generator
  • adding/removing images will change the order of the images - but it is guaranteed that new images are shown before the image sequence starts over

The major reason why I favor this are the first two points.

phillipberndt added a commit that referenced this issue Nov 21, 2014
See bug #30 for an alternative, lengthy explanation. To make shuffle
mode toggleable, we can no longer simply choose random keys for the file
tree, but must instead shuffle in the relative_image_movement()
function.

For users, it is important that shuffle mode doesn't display images that
they have already seen. Also, it is desired that forward and backward
image movement works predictable despite the images being shuffled.

The proposed solution uses a linear congruential PRNG whose parameters
are chosen such that each sequence of random numbers forms a full cycle,
i.e. that no number is chosen twice until all other numbers have been
generated. We invent the formula to allow backwards movement. If an
image is viewed, it is marked as "seen". If the user leaves it via
backward movement, this mark is again removed. If a user moves forward
through the images and shuffle mode is enabled, only images that don't
have the mark set are considered. Once all images have the mark set, it
is removed from all images and the process starts over again.

I am not sure whether this works out, and also not sure if removing the
"seen" flag is a good idea.
@phillipberndt
Copy link
Owner

If you want to test what I suggested above, try compiling from the random_toggle branch:

https://github.com/phillipberndt/pqiv/archive/random_toggle.zip

@SpaceCadet2000
Copy link
Author

Hi Phillip, thanks for the update.

In regards to your question about how it should work:

For me the second option is definitely the most logical. When resuming shuffle mode, I'd like to just continue where I left off and just ignore the fact that some images were already viewed in sequential mode.

phillipberndt added a commit that referenced this issue Dec 3, 2014
See bug #30 for the backgrounds of this change. This commit introduces a
toggleable shuffle mode, where a user can switch off shuffle mode to
view some images in sequential order and then switch it on again and
continue at where he initially disabled it. Commit 0dc33dd contains an
alternative implementation, that also counts the sequentially visited
images as "seen" and is based on a completely different implementation.
Especially it doesn't require us to maintain a separate list of images,
but instead uses an invertible PRNG. See bug #30 for why I eventually
rejected that approach.

This variant works as follows: pqiv maintains a list of images that have
been seen in random order. Relative image movements in shuffle mode take
place within this list. If a movement crosses either end of the list,
the list is dynamically filled up with randomly chosen images that are
not yet present in the list. (In a list deciding this is O(n)!) To speed
things up a little, we do not choose random numbers until we find one
that is not yet present, but instead choose a random one and then
sequentially walk through the image tree until we find one that isn't.
This also generates eqidistributed permutations. (I didn't prove that,
but large samples look as if it does.) Once this list is full, it is
flushed and regenerated. The list contains weak references to the
images, such that the mechanisms that insert/remove files as they are
altered on the disk still works.
@phillipberndt
Copy link
Owner

Good to hear. That option is much simpler, so let's go with that one. I've implemented it in the random_toggle_2 branch. Does this work as you'd expect it to?

Here's the direct download link for the source code: https://github.com/phillipberndt/pqiv/archive/random_toggle_2.zip Let me know if you'd like to have a binary.

@phillipberndt
Copy link
Owner

I'll merge this in the near future. Any change requests? :-)

phillipberndt added a commit that referenced this issue Jan 6, 2015
See bug #30 for the backgrounds of this change. This commit introduces a
toggleable shuffle mode, where a user can switch off shuffle mode to
view some images in sequential order and then switch it on again and
continue at where he initially disabled it. Commit 0dc33dd contains an
alternative implementation, that also counts the sequentially visited
images as "seen" and is based on a completely different implementation.
Especially it doesn't require us to maintain a separate list of images,
but instead uses an invertible PRNG. See bug #30 for why I eventually
rejected that approach.

This variant works as follows: pqiv maintains a list of images that have
been seen in random order. Relative image movements in shuffle mode take
place within this list. If a movement crosses either end of the list,
the list is dynamically filled up with randomly chosen images that are
not yet present in the list. (In a list deciding this is O(n)!) To speed
things up a little, we do not choose random numbers until we find one
that is not yet present, but instead choose a random one and then
sequentially walk through the image tree until we find one that isn't.
This also generates eqidistributed permutations. (I didn't prove that,
but large samples look as if it does.) Once this list is full, it is
flushed and regenerated. The list contains weak references to the
images, such that the mechanisms that insert/remove files as they are
altered on the disk still works.
@phillipberndt
Copy link
Owner

Merged. The current binary (see readme) already includes these changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants