Мне нужно знать, какой тип алгоритма эффективно вычисляет суммы в древовидных структурах с помощью механизма кеширования.

Проблема, которую я хочу решить, проста: есть два типа узлов: Папки (F) и Файлы (L) ... похоже на файловую систему :) ...

Папки и файлы можно создавать и перемещать в древовидной структуре. Файлы меняются по размеру.

Теперь мне нужна функция size(node), которая возвращает размер содержимого узла.

Образец:

F1
 - L1 : 10
 - L2 : 33
F2 
 - F21
    - F211
         - L3 : 2
         - L4 : 8
    - F212
         - F2121
               - L5 : 18
         - F2122
               - L6 : 21
               - L7 : 3            

size(node) gives:

size(F1):43
size(F2):52
size(F21):52
size(F211):10
size(F212):42
size(L5):18
...

Известное решение - рекурсивно складывать :) ...

size(node) 
  int res=0;
  if(node is File)
    return sizeOfFile(node)
  else
    for each child of node
      res += size(child)
    end 
  end
  return res
end

Я спрашиваю себя, могу ли я использовать здесь какой-либо тип кеширования. Какое название и тип алгоритма мне нужно искать?

1
mark61 19 Фев 2016 в 09:35

2 ответа

Лучший ответ

Ты можешь сделать одно. Вы можете вычислить стоимость, когда посетите узел и сохраните его. Подумайте об этом как о хранилище на вашем телефоне или компьютере. Пример здесь был бы лучше. введите описание изображения здесь

Я не назвал здесь стоимость какого-либо узла. Допустим, вы посещаете узел номер 4. Вы вычисляете стоимость, складывая стоимость 6 и 7, и сохраняете ее в базе данных. В базе данных это может храниться как directory path -- cost. Теперь предположим, что вы хотите вычислить стоимость 2. Вам не нужно посещать 4, поскольку вы уже предварительно вычислили стоимость. Вам нужно только вычислить стоимость 5, и тогда все готово. Таким образом, мы сохраняем стоимость 5 и 2 в базе данных. В следующий раз, если вы захотите узнать размер 4 или 5, вы можете просто получить его из БД.

Я предполагаю, что это файловая структура. Так что вам нужно будет чаще заходить в каталоги. Чем больше вы посещаете, тем больше будет производиться запоминание размеров. Тем легче будет вычислить стоимость. В конце концов, если вы измените файлы или переместите их, просто вычислите стоимость только этого узла.

Я думаю, что преимуществом здесь будет то, что вам не нужно будет вычислять стоимость каждый раз, когда вы посещаете узел. вы будете использовать хранилище, которое сделает ваши вычисления размера намного быстрее со временем.

Надеюсь, это имеет смысл.

1
Shubhashis 19 Фев 2016 в 07:10

Прикрепил реализацию с использованием быстрых и основных данных. Он генерирует случайные данные дерева в базе данных core data / sqlite и перемещает некоторые узлы (папки) в другие целевые папки. Результат выглядит так:

Building the tree ...
insert file nodes: [0(15): 1  100]
insert file nodes: [1(15): 101  200]
insert file nodes: [2(15): 201  300]
insert file nodes: [3(15): 301  400]
insert file nodes: [4(15): 401  500]
insert file nodes: [5(15): 501  600]
insert file nodes: [6(15): 601  700]
insert file nodes: [7(15): 701  800]
insert file nodes: [8(15): 801  900]
insert file nodes: [9(15): 901  1000]
insert file nodes: [10(15): 1001  1100]
insert file nodes: [11(15): 1101  1200]
insert file nodes: [12(15): 1201  1300]
insert file nodes: [13(15): 1301  1400]
insert file nodes: [14(15): 1401  1500]
Congratulations! Tree exists. It's initial size is:75563
Moving F000120 to target folder F000103 ...
before moving:
size[F000120] = 607 path = /F000001/F000003/
source folder: size[F000003] = 37388
target folder: size[F000103] = 1134
after moving:
size[F000120] = 607 path = /F000001/F000003/F000014/F000030/F000072/F000103/
source folder: size[F000003] = 37995
target folder: size[F000103] = 1741
Moving F000200 to target folder F000209 ...
before moving:
size[F000200] = 224 path = /F000001/F000016/
source folder: size[F000016] = 1686
target folder: size[F000209] = 215
after moving:
size[F000200] = 224 path = /F000001/F000002/F000006/F000007/F000041/F000209/
source folder: size[F000016] = 1462
target folder: size[F000209] = 439
Moving the nodes completed. Tree size is:75563

Основная модель данных:

enter image description here

AppDelegate:

//
//  AppDelegate.swift
//  SizesNTrees
//
//  Created by Markus Schmid on 20.02.16.
//

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    // MARK: - Core Data stack

    lazy var applicationDocumentsDirectory: NSURL = {
        let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
        return urls[urls.count-1]
    }()

    lazy var managedObjectModel: NSManagedObjectModel = {
        let modelURL = NSBundle.mainBundle().URLForResource("SizesNTrees", withExtension: "momd")!
        return NSManagedObjectModel(contentsOfURL: modelURL)!
    }()

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SizesNTrees.sqlite")
        var failureReason = "There was an error creating or loading the application's saved data."
        do {
            try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
        } catch {
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason

            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }

        return coordinator
    }()

    lazy var managedObjectContext: NSManagedObjectContext = {
        let coordinator = self.persistentStoreCoordinator
        var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
    }()

    // MARK: - Core Data Saving support

    func saveContext () {
        if managedObjectContext.hasChanges {
            do {
                try managedObjectContext.save()
            } catch {
                let nserror = error as NSError
                NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                abort()
            }
        }
    }

}

ViewController:

//
//  ViewController.swift
//  SizesNTrees
//
//  Created by Markus Schmid on 20.02.16.
//

import UIKit
import CoreData

enum NodeType: Int {
    case File
    case Folder
}

var node : Node!

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        if let root=nodeWithName("F000001") {
            print("Tree exists. Initial tree size is:\(root.size!)")
        }
        else {
            print("Building the tree ...")
            buildTree(300, noFiles:1500)
        }
        if let root=nodeWithName("F000001") {
            print("Congratulations! Tree exists. It's initial size is:\(root.size!)")
            moveNode("F000120",targetFolderName:"F000103")
            moveNode("F000200",targetFolderName:"F000209")
            print("Moving the nodes completed. Tree size is:\(root.size!)")
        }
        else {
            print("Tree is missing.")
            return
        }
        do {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            let managedContext = appDelegate.managedObjectContext
            try managedContext.save()
        } catch let error as NSError {
            print("Could not save \(error), \(error.userInfo)")
        }

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func moveNode(nodeName:String, targetFolderName:String) {
        // try to move move nodeName to targetFolderName
        if let node=nodeWithName(nodeName) {
            if let targetFolder=nodeWithName(targetFolderName) {
                print ("Moving \(nodeName) to target folder \(targetFolderName) ... ")
                print("before moving:")
                print("   size[\(node.name!)] = \(node.size!) path = \(path(node))")
                let origParent=node.parent
                print("   source folder: size[\(origParent!.name!)] = \(origParent!.size!)")
                print("   target folder: size[\(targetFolder.name!)] = \(targetFolder.size!)")
                if !isDescendantOrSelf(targetFolder, ascendant: node) {
                    move(node, toFolder: targetFolder)
                    print("after moving:")
                    print("   size[\(node.name!)] = \(node.size!) path = \(path(node))")
                    print("   source folder: size[\(origParent!.name!)] = \(origParent!.size!)")
                    print("   target folder: size[\(targetFolder.name!)] = \(targetFolder.size!)")
                }
                else {
                    print ("Moving folder to target folder failed! Target folder is descendant from folder.")
                }
            }
            else {
                print ("Target folder \(targetFolderName) not found!")
            }
        }
        else {
            print ("Node \(nodeName) not found!")
        }
    }

    func buildTree(noFolders:Int,noFiles:Int) {
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let nodeEntity =  NSEntityDescription.entityForName("Node", inManagedObjectContext:managedContext)
        // create folders
        autoreleasepool {
            for index in 1...noFolders {
                node = NSManagedObject(entity: nodeEntity!, insertIntoManagedObjectContext: managedContext) as! Node
                node.name=String(format: "F%06d", index)
                node.size=0
                node.type=NodeType.Folder.rawValue
                node.nr=index
                if index>1 {
                    node.parent=nodeOfType(NodeType.Folder.rawValue, nr:Int(arc4random_uniform(UInt32(index-1))+1))
                }
            }
            do {
                try managedContext.save()
            } catch let error as NSError {
                print("Could not save \(error), \(error.userInfo)")
            }
            managedContext.reset()
        }
        // create files
        let bSize = 100
        let noB = noFiles/bSize
        var index = 0
        for var bNr=0;(bNr<noB || ((bNr*bSize<noFiles) && (((bNr+1)*bSize)>noFiles)) );bNr++ {
            autoreleasepool {
                var startIndex=0
                var maxIndex=0
                if bNr<noB {
                    startIndex=bNr*bSize+1
                    maxIndex=(bNr+1)*bSize
                }
                else {
                    startIndex=bNr*bSize+1
                    maxIndex=noFiles
                }
                print("insert file nodes: [\(bNr)(\(noB)): \(startIndex)  \(maxIndex)]")
                for index=startIndex;index<maxIndex+1;index++ {
                    let node = NSManagedObject(entity: nodeEntity!, insertIntoManagedObjectContext: managedContext) as! Node
                    node.name=String(format: "L%06d", index)
                    node.size=Int(arc4random_uniform(100)+1)
                    node.type=NodeType.File.rawValue
                    node.nr=index
                    node.parent=self.nodeOfType(NodeType.Folder.rawValue, nr:Int(arc4random_uniform(UInt32(noFolders))+1))
                }
                do {
                    try managedContext.save()
                } catch let error as NSError {
                    print("Could not save \(error), \(error.userInfo)")
                }
                managedContext.reset()
            }
        }

        fillSize();
    }

    func nodeWithName(name:String, context:NSManagedObjectContext) -> Node? {
        var node : Node?
        let managedContext = context
        let predicate = NSPredicate(format:"name = %@", name)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }

    func nodeOfType(type:NSNumber, nr:NSNumber, context:NSManagedObjectContext) -> Node? {
        var node : Node?
        let managedContext = context
        let predicate = NSPredicate(format:"type = %@ AND nr = %@", type, nr)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }

    func nodeWithName(name:String) -> Node? {
        var node : Node?
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let predicate = NSPredicate(format:"name = %@", name)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }


    func nodeOfType(type:NSNumber, nr:NSNumber) -> Node? {
        var node : Node?
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let predicate = NSPredicate(format:"type = %@ AND nr = %@", type, nr)
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate

        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            if fetchedNodes.count>0 {
                node=fetchedNodes[0]
            }
            else {
                node=nil
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        return node
    }

    func fillSize() {
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext
        let predicate = NSPredicate(format:"type = 1")
        let request = NSFetchRequest(entityName:"Node")
        request.predicate=predicate
        do {
            let fetchedNodes = try managedContext.executeFetchRequest(request) as! [Node]
            for node in fetchedNodes {
                node.size=size(node);
            }
        } catch {
            fatalError("Failed to fetch nodes: \(error)")
        }
        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Could not save \(error), \(error.userInfo)")
        }
    }

    func size(node:Node) -> Int {
        var res : Int = 0
        if node.type == 0 {
            res=(node.size?.integerValue)!;
        }
        else {
            if let nodeChilds=node.childs {
                for child in nodeChilds {
                    res = res + size(child);
                }
            }
        }
        return res;
    }

    func path(node:Node) -> String {
        if let parent=node.parent {
            return path(parent).stringByAppendingString((node.parent?.name)!).stringByAppendingString("/")
        }
        else {
            return "/"
        }
    }

    func move(node:Node, toFolder:Node) -> Bool {
        var success=false
        if let size=node.size {
            if let parent=node.parent {
                if parent.isEqual(toFolder) {
                    return true
                }
                else {
                    propagateSizeChange(-size.integerValue, parent: parent)
                }
            }
            propagateSizeChange(size.integerValue, parent: toFolder)
            node.parent=toFolder
            success=true
        }
        return success
    }

    func setSizeFor(file:Node, var newSize:Int) {
        if newSize<=0 {
            newSize=0
        }
        if let size=node.size {
            if let parent=node.parent {
                propagateSizeChange(newSize-size.integerValue, parent: parent)
            }
        }
    }

    func isDescendantOrSelf(node:Node, ascendant:Node) -> Bool {
        var success=false
        if ascendant.isEqual(node) {
            return true
        }
        else {
            if let nodeChilds=ascendant.childs {
                if nodeChilds.contains(node) {
                    return true;
                }
                else {
                    for child in nodeChilds {
                        success = (success || self.isDescendantOrSelf(node,ascendant:child))
                        if success {
                            break;
                        }
                    }
                }
            }
        }
        return success
    }

    func propagateSizeChange(value:Int, parent:Node) {
        if parent.type == 1 {
            parent.size = NSNumber(integer:(parent.size!.integerValue + value))
            if let parent=parent.parent {
                parent.size = NSNumber(integer:(parent.size!.integerValue + value))
                propagateSizeChange(value, parent: parent)
            }
        }
    }
}
0
mark61 21 Фев 2016 в 19:35