Find a subsequence in a list - kotlin

Lets assume that we are looking for a sequence in a list and this sequence should satisfy some conditions, for example I have a series of numbers like this:
[1,2,4,6,7,8,12,13,14,15,20]
I need to find the largest sequence so that its consecutive elements have a difference of 1, So what I expected to get is:
[12,13,14,15]
I'm curious if there is any way to get in Kotlin Sequence or inline functions like groupBy or something else.
PS: I know how to create sequences, the question is how to evaluate and extract some sequences with given conditions.

There is no built in functionality for this "sequence" recognition, but you could solve it with the fold operation:
val result = listOf(1, 2, 3, 12, 13, 14, 15)
.distinct() // remove duplicates
.sorted() // by lowest first
.fold(mutableListOf<Int>() to mutableListOf<List<Int>>()) { (currentList, allLists), currentItem ->
if (currentList.isEmpty()) { // Applies only to the very first item
mutableListOf(currentItem) to allLists
} else {
if (currentItem - currentList.max()!! == 1) { // Your custom sequence recognition 'difference of 1'
currentList.apply { add(currentItem) } to allLists
} else {
mutableListOf(currentItem) to allLists.apply { add(currentList) } // Next
}
}
}
.let { it.second.apply { add(it.first) } } // Add last list
.maxBy { it.size } // We need the longest pattern - which will take first of the stack - it could be multiple.
// If you need more precise list, sort them by your criteria

Related

How can I optimize finding the shortest description of a set

The goal is to find the optimal description of a subset of countries, using a combination of individual countries and groups of countries that are predefined.
Set of countries is comprised of all 2 letter ISO country codes, (US/DE/NL/etc)
Groups of countries are fixed, for example:
GROUP1: NL,BE,LU
GROUP2: NL,BE,LU,CZ,US,FI,NO,GI,FR,DK
I want to have a representation of this list of countries:
CZ,US,FI,NO,GI,FR,DK,DE
I would like to shorten this using the defined groups, to the representation with the least amount of elements. For this example that would be:
+GROUP2 +DE -GROUP1
Because if we expand this back out we get:
Add GROUP2: NL,BE,LU,CZ,US,FI,NO,GI,FR,DK
Add DE
Remove GROUP1: NL,BE,LU
Which brings us back to CZ,US,FI,NO,GI,FR,DK,DE
Any pointers to libraries or examples are welcome in any programming language
I'm at a loss what a good approach would be. I've tried looking into existing algorithms, comparable problems, but I can't seem to wrap my head around the problem and find a good fit.
Just to illustrate a simple brute force solution that will obviously not scale:
let countries = ['C1', 'C2', 'C3', 'C4', 'C5', 'C6']
let groups = {
G1: ['C1', 'C2', 'C3', 'C4', 'C5'],
G2: ['C1', 'C4'],
}
let output = getBest(['C2', 'C3', 'C5', 'C6'])
// output == ["+C6", "+G1", "-G2"]
function getBest(input) {
const ids = countries.concat(Object.keys(groups))
let best = input
for (const t of combinations(ids)) {
if (expand(t, groups).sort().toString() == input.toString()) {
if (t.length < best.length) best = [...t]
}
}
return best
}
// Expands short form to long form
function expand(s, groups) {
return Array.from(
s.sort().reduce((acc, curr) => {
let symbol = curr[0]
let id = curr.slice(1)
if (groups[id]) {
curr = groups[id]
} else {
curr = [id]
}
if (symbol == '+') {
return new Set([...acc, ...curr])
} else {
return new Set([...acc].filter((a) => !curr.includes(a)))
}
}, new Set())
)
}
// Yields all possible short form options
function* combinations(array) {
array = ['+', '-'].reduce((acc, curr) => {
return acc.concat(array.map((s) => curr + s))
}, [])
for (const subset of subsets(array)) {
yield subset
}
}
// Creates powerset of array
function* subsets(array, offset = 0) {
while (offset < array.length) {
let first = array[offset++]
for (let subset of subsets(array, offset)) {
subset.push(first)
yield subset
}
}
yield []
}
To me, the problem does not sound like there exists a classical model for it with a well-known polynomial-time algorithm.
A reasonable approach looks as follows.
Consider formulas in a formal language, like (G1 subtract G2) unite (G3 intersect G4), where Gi are predefined groups (and perhaps individual countries, but that will slow the solution down a lot).
Each formula's score is its length plus the size of difference with the desired answer (so that we add or subtract individual elements as the last step of the formula).
Now, for formulas of lengths 0, 1, 2, ..., (iterative deepening), recursively generate all possible formulas of such length and consider their score.
Stop when the length reaches the score of the best answer so far.
There's some room to optimize (for example, prune clearly stupid branches like Gi symdiff Gi, or perhaps memoize the shortest formula for each set we can obtain), but the solution is nevertheless exponential in the number of items and groups.

How do I write this for-loop in functional style?

I have a function with a for-loop:
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
var sum = 0
for (item in this) {
if (sumFunction(item))
sum += item
}
return sum
}
I want to know how I can write the above in functional style. I know that I have to use this.reduce(), but don't know exactly how to implement it.
return filter(sumFunction).sum()
Should be self-explanatory.
You can’t use reduce because it doesn’t let you reject the first element.
With fold it would be:
return fold(0) { a, b ->
if(sumFunction(b)) a + b else a
}
I can think if two ways to achieve that:
The first one is by using sumOf {...}:
.
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
return sumOf {
if (sumFunction(it)) it else 0
}
}
The second one is by using filter {...} then sum():
.
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
return filter(sumFunction).sum()
}
return this.reduce { sum, n -> if (sumFunction(n)) sum + n else 0}
If you really want to use reduce for some reason you can - but you need to add that 0 to the head of the list as your "start state":
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
val stuff = listOf(0) + this
return stuff.reduce { a, b -> a + if (sumFunction(b)) b else 0 }
}
You have to do that because reduce is really there to combine a bunch of items, which is why for the first iteration you get the first two items in the list. You don't get to handle them separately, which is why you need to throw that 0 in there to get past that first step, and get to a point where you can just do your checking on the second parameter and ignore the first one, treating it as an accumulator instead of another item you also need to check.
That behaviour is what fold is for - with that function you pass in an initial state (which can be a completely different type from your items, since you're not just smushing them together to create a new value like with reduce) and then on each iteration you get that state and an item.
You can handle the item as you like, and then make changes to the accumulator state depending on the result. Which is exactly the behaviour of your for loop! fold is just a functional way to write one. Tenfour04's answer is how you'd do it - it's the right tool for the job here!

Run a regex on a Supply or other stream-like sequence?

Suppose I have a Supply, Channel, IO::Handle, or similar stream-like source of text, and I want to scan it for substrings matching a regex. I can't be sure that matching substrings do not cross chunk boundaries. The total length is potentially infinite and cannot be slurped into memory.
One way this would be possible is if I could instantiate a regex matching engine and feed it chunks of text while it maintains its state. But I don't see any way to do that -- I only see methods to run the match engine to completion.
Is this possible?
After some more searching, I may have answered my own question. Specifically, it seems Seq.comb is capable of combining chunks and lazily processing them:
my $c = supply {
whenever Supply.interval(1.0) -> $v {
my $letter = do if ($v mod 2 == 0) { "a" } else { "b" };
my $chunk = $letter x ($v + 1);
say "Pushing {$chunk}";
emit($chunk);
}
};
my $c2 = $c.comb(/a+b+/);
react {
whenever $c2 -> $v {
say "Got {$v}";
}
}
See also the concurrency features used to construct this example.

Write a kotlin program that prints the number that is repeated the most in a consecutive way

I'm kind of stuck, I don't know how to make the second loop to start 1 position above the first loop in Kotlin.
I have an array (named myArray) with 10 elements, I need to Write a Kotlin program that prints the number that has the most consecutive repeated number in the array and also prints the number of times it appears in the sequence.
The program must parse the array from left to right so that if two numbers meet the condition, the one that appears first from left to right will be printed.
Longest: 3
Number: 8
fun main() {
val myArray: IntArray = intArrayOf(1,2,2,4,5,6,7,8,8,8)
for((index , value) in myArray.withIndex()){
var inx = index + 1
var count = 0
var longest = 0
var number = 0
for((inx,element) in myArray.withIndex()) {
if(value == element ){
count+=
}
}
if(longest < count){
longest = count
number = value
}
}
}
I'm against just dropping answers, but it is quite late for me, so I'll leave this answer here and edit it tomorrow with more info on how each part works. I hope that maybe in the meanwhile it will help you to gain some idea to where you might be going wrong.
val results = mutableMapOf<Int, Int>()
(0..myArray.size - 2).forEach { index ->
val current = myArray[index]
if (current == myArray[index + 1]) {
results[current] = (results[current] ?: 1) + 1
}
}
val (max, occurrences) = results.maxByOrNull { it.value } ?: run { println("No multiple occurrences"); return }
println("Most common consecutive number $max, with $occurrences occurrences")
Alternatively if the intArray would be a list, or if we allowed to change it to a list myArray.toList(), you could replace the whole forEach loop with a zipWithNext. But I'm pretty sure that this is a HW question, so I doubt this is the expected way of solving it.
myList.zipWithNext { a, b ->
if (a == b) results[a] = (results[a] ?: 1) + 1
}

How to properly iterate over arrays in kotlin

I am currently learning kotlin and therefore following the kotlin track on exercism. The following exercise required me to calculate the Hamming difference between two Strings (so basically just counting the number of differences).
I got to the solution with the following code:
object Hamming {
fun compute(dnaOne: String, dnaTwo: String): Int {
if (dnaOne.length != dnaTwo.length) throw IllegalArgumentException("left and right strands must be of equal length.")
var counter = 0
for ((index, letter) in dnaOne.toCharArray().withIndex()) {
if (letter != dnaTwo.toCharArray()[index]) {
counter++
}
}
return counter
}
}
however, in the beginning I tried to do dnaOne.split("").withIndex() instead of dnaOne.toCharArray().withIndex() which did not work, it would literally stop after the first iteration and the following example
Hamming.compute("GGACGGATTCTG", "AGGACGGATTCT") would return 1 instead of the correct integer 9 (which only gets returned when using toCharArray)
I would appreciate any explanation
I was able to simplify this by using the built-in CharSequence.zip function because StringimplementsCharSequence` in Kotlin.
According to the documentation for zip:
Returns a list of pairs built from the characters of this and the [other] char sequences with the same index
The returned list has length of the shortest char sequence.
Which means we will get a List<Pair<Char,Char>> back (a list of pairs of letters in the same positions). Now that we have this, we can use Iterable.count to determine how many of them are different.
I implemented this as an extension function on String rather than in an object:
fun String.hamming(other: String): Int =
if(this.length != other.length) {
throw IllegalArgumentException("String lengths must match")
} else {
this.zip(other).count { it.first != it.second }
}
This also becomes a single expression now.
And to call this:
val ham = "GGACGGATTCTG".hamming("AGGACGGATTCT")
println("Hamming distance: $ham")