Skip to content

Improve fourSum function with unique quadruplets #1008

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 38 additions & 34 deletions 3Sum and 4Sum/4Sum.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,7 @@
print("Hello, Swift 4.2!")
#endif

extension Collection where Element: Equatable {

/// In a sorted collection, replaces the given index with a successor mapping to a unique element.
///
/// - Parameter index: A valid index of the collection. `index` must be less than `endIndex`
func formUniqueIndex(after index: inout Index) {
var prev = index
repeat {
prev = index
formIndex(after: &index)
} while index < endIndex && self[prev] == self[index]
}
}


extension BidirectionalCollection where Element: Equatable {

Expand All @@ -32,33 +20,49 @@ extension BidirectionalCollection where Element: Equatable {
}

func fourSum<T: BidirectionalCollection>(_ collection: T, target: T.Element) -> [[T.Element]] where T.Element: Numeric & Comparable {
let sorted = collection.sorted()
var ret: [[T.Element]] = []
let sorted = collection.sorted()
var ret: [[T.Element]] = []

var l = sorted.startIndex
FourSum: while l < sorted.endIndex { defer { sorted.formUniqueIndex(after: &l) }
var ml = sorted.index(after: l)
var l = sorted.startIndex
FourSum: while l < sorted.endIndex {
// Skip over any repeated elements
while l < sorted.endIndex - 1, sorted[l] == sorted[sorted.index(after: l)] {
sorted.formIndex(after: &l)
}
defer { sorted.formIndex(after: &l) }
var ml = sorted.index(after: l)

ThreeSum: while ml < sorted.endIndex { defer { sorted.formUniqueIndex(after: &ml) }
var mr = sorted.index(after: ml)
var r = sorted.index(before: sorted.endIndex)
ThreeSum: while ml < sorted.endIndex {
// Skip over any repeated elements
while ml < sorted.endIndex - 1, sorted[ml] == sorted[sorted.index(after: ml)] {
sorted.formIndex(after: &ml)
}
defer { sorted.formIndex(after: &ml) }
var mr = sorted.index(after: ml)
var r = sorted.index(before: sorted.endIndex)

TwoSum: while mr < r && r < sorted.endIndex {
let sum = sorted[l] + sorted[ml] + sorted[mr] + sorted[r]
if sum == target {
ret.append([sorted[l], sorted[ml], sorted[mr], sorted[r]])
sorted.formUniqueIndex(after: &mr)
sorted.formUniqueIndex(before: &r)
} else if sum < target {
sorted.formUniqueIndex(after: &mr)
} else {
sorted.formUniqueIndex(before: &r)
TwoSum: while mr < r && r < sorted.endIndex {
let sum = sorted[l] + sorted[ml] + sorted[mr] + sorted[r]
if sum == target {
ret.append([sorted[l], sorted[ml], sorted[mr], sorted[r]])
// Skip over any repeated elements
while mr < r, sorted[mr] == sorted[sorted.index(after: mr)] {
sorted.formIndex(after: &mr)
}
while mr < r, sorted[r] == sorted[sorted.index(before: r)] {
sorted.formIndex(before: &r)
}
} else if sum < target {
sorted.formIndex(after: &mr)
} else {
sorted.formIndex(before: &r)
}
}
}
}
}
}
return ret
return ret
}


// answer: [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]
fourSum([1, 0, -1, 0, -2, 2], target: 0)
108 changes: 55 additions & 53 deletions Bloom Filter/BloomFilter.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -1,82 +1,84 @@
//: Playground - noun: a place where people can play

public class BloomFilter<T> {
fileprivate var array: [Bool]
private var hashFunctions: [(T) -> Int]

public init(size: Int = 1024, hashFunctions: [(T) -> Int]) {
self.array = [Bool](repeating: false, count: size)
self.hashFunctions = hashFunctions
}

private func computeHashes(_ value: T) -> [Int] {
return hashFunctions.map { hashFunc in abs(hashFunc(value) % array.count) }
}

public func insert(_ element: T) {
for hashValue in computeHashes(element) {
array[hashValue] = true
import Foundation

public class BloomFilter<T: Hashable> {
fileprivate var array: [Bool]
private var hashFunctions: [(T) -> Int]

public init(size: Int = 1024, hashFunctions: [(T) -> Int]) {
if (hashFunctions.count > size) {
fatalError("Number of hash functions should be less than or equal to size of array")
}
self.array = [Bool](repeating: false, count: size)
self.hashFunctions = hashFunctions
}
}

public func insert(_ values: [T]) {
for value in values {
insert(value)
private func computeHashes(_ value: T) -> [Int] {
return hashFunctions.map { hashFunc in abs(hashFunc(value) % array.count) }
}
}

public func query(_ value: T) -> Bool {
let hashValues = computeHashes(value)
public func insert(_ element: T) {
for hashValue in computeHashes(element) {
array[hashValue] = true
}
}

// Map hashes to indices in the Bloom Filter
let results = hashValues.map { hashValue in array[hashValue] }
public func insert(_ values: [T]) {
for value in values {
insert(value)
}
}

// All values must be 'true' for the query to return true
public func query(_ value: T) -> Bool {
let hashValues = computeHashes(value)

// This does NOT imply that the value is in the Bloom filter,
// only that it may be. If the query returns false, however,
// you can be certain that the value was not added.
// Map hashes to indices in the Bloom Filter
let results = hashValues.map { hashValue in array[hashValue] }

let exists = results.reduce(true, { $0 && $1 })
return exists
}
// All values must be 'true' for the query to return true
// This does NOT imply that the value is in the Bloom filter,
// only that it may be. If the query returns false, however,
// you can be certain that the value was not added.
let exists = results.reduce(true, { $0 && $1 })
return exists
}

public func isEmpty() -> Bool {
// As soon as the reduction hits a 'true' value, the && condition will fail.
return array.reduce(true) { prev, next in prev && !next }
}
public func isEmpty() -> Bool {
// As soon as the reduction hits a 'true' value, the && condition will fail.
return array.reduce(true) { prev, next in prev && !next }
}
}

/* Two hash functions, adapted from http://www.cse.yorku.ca/~oz/hash.html */

func djb2(x: String) -> Int {
var hash = 5381
for char in x {
hash = ((hash << 5) &+ hash) &+ char.hashValue
}
return Int(hash)
var hash = 5381
for char in x {
hash = ((hash << 5) &+ hash) &+ char.hashValue
}
return Int(hash)
}

func sdbm(x: String) -> Int {
var hash = 0
for char in x {
hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash
}
return Int(hash)
var hash = 0
for char in x {
hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash
}
return Int(hash)
}

/* A simple test */

let bloom = BloomFilter<String>(size: 17, hashFunctions: [djb2, sdbm])

bloom.insert("Hello world!")
print(bloom.array)

bloom.query("Hello world!") // true
bloom.query("Hello WORLD") // false
(bloom.query("Hello world!")) // true
(bloom.query("Hello WORLD")) // false

bloom.insert("Bloom Filterz")
print(bloom.array)
(bloom.array)

(bloom.query("Bloom Filterz")) // true
(bloom.query("Hello WORLD")) // false

bloom.query("Bloom Filterz") // true
bloom.query("Hello WORLD") // true